Compare commits
43 Commits
feature/20
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| bd9fdeb3d9 | |||
| 6c633de22b | |||
| f64c59b9a1 | |||
| a4adb4fb03 | |||
| 985708e87a | |||
| 01cef7a595 | |||
| 18ab1f6ae3 | |||
| 5ea3ac14e4 | |||
| 84079df772 | |||
| 28a8b0b5d1 | |||
|
|
2c05785096 | ||
|
|
6af4be9c6c | ||
|
|
8def1d31d2 | ||
|
|
afab02a053 | ||
|
|
8e925c62f2 | ||
|
|
1b7d513adf | ||
|
|
7d764fb390 | ||
|
|
c571f50a74 | ||
|
|
6549026344 | ||
|
|
0963394b04 | ||
|
|
6cab0651fc | ||
|
|
4af537d6dd | ||
|
|
34db64612c | ||
|
|
10225bbfed | ||
|
|
c3a4fe6b37 | ||
|
|
02be959461 | ||
|
|
d5bf56e333 | ||
|
|
e3182c15b3 | ||
|
|
dfa63ece21 | ||
|
|
6e95bd7da1 | ||
|
|
f51fffa5c7 | ||
|
|
6193358cc3 | ||
|
|
518abcd30d | ||
|
|
7874b9f765 | ||
|
|
02cc7e0c96 | ||
|
|
57cc513873 | ||
|
|
f5bfdede30 | ||
|
|
488556bb59 | ||
|
|
0fc5a66e95 | ||
|
|
d9ae51027e | ||
|
|
a92d6112d9 | ||
|
|
889b0cec4b | ||
|
|
1c4c41ebba |
93
.dockerignore
Normal file
93
.dockerignore
Normal file
@@ -0,0 +1,93 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
**/node_modules/
|
||||
.pnpm-store/
|
||||
|
||||
# Build outputs
|
||||
dist/
|
||||
build/
|
||||
**/dist/
|
||||
**/build/
|
||||
*.tsbuildinfo
|
||||
|
||||
# Development files
|
||||
.git/
|
||||
.gitignore
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Test files
|
||||
test/
|
||||
**/test/
|
||||
**/*.spec.ts
|
||||
**/*.test.ts
|
||||
**/*.e2e-spec.ts
|
||||
coverage/
|
||||
.nyc_output/
|
||||
test-results/
|
||||
playwright-report/
|
||||
|
||||
# Documentation
|
||||
*.md
|
||||
!README.md
|
||||
docs/
|
||||
CHANGELOG.md
|
||||
CONTRIBUTING.md
|
||||
DISCLAIMER
|
||||
LICENSE
|
||||
|
||||
# CI/CD
|
||||
.github/
|
||||
.gitpod.yml
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
logs/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Docker files (don't copy Dockerfiles into themselves)
|
||||
docker-compose*.yml
|
||||
Dockerfile*
|
||||
.dockerignore
|
||||
|
||||
# Misc
|
||||
.cache/
|
||||
.temp/
|
||||
tmp/
|
||||
*.tmp
|
||||
.qodo/
|
||||
e2e/
|
||||
playwright.config.ts
|
||||
|
||||
# Source maps (not needed in production)
|
||||
*.map
|
||||
|
||||
# TypeScript configs (not needed at runtime)
|
||||
tsconfig*.json
|
||||
!tsconfig.json
|
||||
|
||||
# Linting/formatting
|
||||
.eslintrc*
|
||||
.prettierrc*
|
||||
.eslintcache
|
||||
|
||||
# Package manager locks (we copy them explicitly)
|
||||
# pnpm-lock.yaml
|
||||
@@ -9,21 +9,21 @@ services:
|
||||
- server
|
||||
- webapp
|
||||
ports:
|
||||
- '${PUBLIC_PROXY_PORT:-80}:80'
|
||||
- '${PUBLIC_PROXY_SSL_PORT:-443}:443'
|
||||
- '8085:80'
|
||||
- '8443:443'
|
||||
tty: true
|
||||
volumes:
|
||||
- ./docker/envoy/envoy.yaml:/etc/envoy/envoy.yaml
|
||||
restart: on-failure
|
||||
networks:
|
||||
- bigcapital_network
|
||||
- coolify
|
||||
|
||||
webapp:
|
||||
container_name: bigcapital-webapp
|
||||
image: bigcapitalhq/webapp:latest
|
||||
restart: on-failure
|
||||
networks:
|
||||
- bigcapital_network
|
||||
- coolify
|
||||
|
||||
server:
|
||||
container_name: bigcapital-server
|
||||
@@ -38,7 +38,7 @@ services:
|
||||
- redis
|
||||
restart: on-failure
|
||||
networks:
|
||||
- bigcapital_network
|
||||
- coolify
|
||||
environment:
|
||||
# Mail
|
||||
- MAIL_HOST=${MAIL_HOST}
|
||||
@@ -58,6 +58,12 @@ services:
|
||||
# System database
|
||||
- SYSTEM_DB_NAME=${SYSTEM_DB_NAME}
|
||||
|
||||
# Redis
|
||||
- REDIS_HOST=redis
|
||||
- REDIS_PORT=6379
|
||||
- QUEUE_HOST=redis
|
||||
- QUEUE_PORT=6379
|
||||
|
||||
# Tenants databases
|
||||
- TENANT_DB_NAME_PERFIX=${TENANT_DB_NAME_PERFIX}
|
||||
|
||||
@@ -138,8 +144,8 @@ services:
|
||||
depends_on:
|
||||
- mysql
|
||||
networks:
|
||||
- bigcapital_network
|
||||
|
||||
- coolify
|
||||
|
||||
mysql:
|
||||
container_name: bigcapital-mysql
|
||||
restart: on-failure
|
||||
@@ -152,10 +158,12 @@ services:
|
||||
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
|
||||
volumes:
|
||||
- mysql:/var/lib/mysql
|
||||
ports:
|
||||
- '3335:3306'
|
||||
expose:
|
||||
- '3306'
|
||||
networks:
|
||||
- bigcapital_network
|
||||
- coolify
|
||||
|
||||
redis:
|
||||
container_name: bigcapital-redis
|
||||
@@ -167,14 +175,14 @@ services:
|
||||
volumes:
|
||||
- redis:/data
|
||||
networks:
|
||||
- bigcapital_network
|
||||
- coolify
|
||||
|
||||
gotenberg:
|
||||
image: gotenberg/gotenberg:7
|
||||
expose:
|
||||
- '9000'
|
||||
networks:
|
||||
- bigcapital_network
|
||||
- coolify
|
||||
|
||||
# Volumes
|
||||
volumes:
|
||||
@@ -188,5 +196,5 @@ volumes:
|
||||
|
||||
# Networks
|
||||
networks:
|
||||
bigcapital_network:
|
||||
driver: bridge
|
||||
coolify:
|
||||
external: true
|
||||
|
||||
@@ -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 ./build/commands.js system:migrate:latest && node ./build/commands.js tenants:migrate:latest"
|
||||
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"
|
||||
|
||||
@@ -1,100 +1,102 @@
|
||||
FROM node:18.16.0-alpine as build
|
||||
# Stage 1: Build
|
||||
FROM node:18.16.0-alpine AS builder
|
||||
|
||||
USER root
|
||||
|
||||
ARG MAIL_HOST= \
|
||||
MAIL_USERNAME= \
|
||||
MAIL_PASSWORD= \
|
||||
MAIL_PORT= \
|
||||
MAIL_SECURE= \
|
||||
MAIL_FROM_NAME= \
|
||||
MAIL_FROM_ADDRESS= \
|
||||
# Database
|
||||
DB_HOST= \
|
||||
DB_USER= \
|
||||
DB_PASSWORD= \
|
||||
DB_CHARSET= \
|
||||
# System database.
|
||||
SYSTEM_DB_NAME= \
|
||||
SYSTEM_DB_PASSWORD= \
|
||||
SYSTEM_DB_USER= \
|
||||
SYSTEM_DB_HOST= \
|
||||
SYSTEM_DB_CHARSET= \
|
||||
# Tenant databases.
|
||||
TENANT_DB_USER= \
|
||||
TENANT_DB_PASSWORD= \
|
||||
TENANT_DB_HOST= \
|
||||
TENANT_DB_NAME_PERFIX= \
|
||||
TENANT_DB_CHARSET= \
|
||||
# Authentication
|
||||
JWT_SECRET= \
|
||||
# Application
|
||||
BASE_URL= \
|
||||
# Sign-up restriction
|
||||
SIGNUP_DISABLED= \
|
||||
SIGNUP_ALLOWED_DOMAINS= \
|
||||
SIGNUP_ALLOWED_EMAILS=
|
||||
|
||||
ENV MAIL_HOST=$MAIL_HOST \
|
||||
MAIL_USERNAME=$MAIL_USERNAME \
|
||||
MAIL_PASSWORD=$MAIL_PASSWORD \
|
||||
MAIL_PORT=$MAIL_PORT \
|
||||
MAIL_SECURE=$MAIL_SECURE \
|
||||
MAIL_FROM_NAME=$MAIL_FROM_NAME \
|
||||
MAIL_FROM_ADDRESS=$MAIL_FROM_ADDRESS \
|
||||
# Database
|
||||
DB_HOST=$DB_HOST \
|
||||
DB_USER=$DB_USER \
|
||||
DB_PASSWORD=$DB_PASSWORD \
|
||||
DB_CHARSET=$DB_CHARSET \
|
||||
# System database.
|
||||
SYSTEM_DB_HOST=$SYSTEM_DB_HOST \
|
||||
SYSTEM_DB_USER=$SYSTEM_DB_USER \
|
||||
SYSTEM_DB_PASSWORD=$SYSTEM_DB_PASSWORD \
|
||||
SYSTEM_DB_NAME=$SYSTEM_DB_NAME \
|
||||
SYSTEM_DB_CHARSET=$SYSTEM_DB_CHARSET \
|
||||
# Tenant databases.
|
||||
TENANT_DB_NAME_PERFIX=$TENANT_DB_NAME_PERFIX \
|
||||
TENANT_DB_HOST=$TENANT_DB_HOST \
|
||||
TENANT_DB_PASSWORD=$TENANT_DB_PASSWORD \
|
||||
TENANT_DB_USER=$TENANT_DB_USER \
|
||||
TENANT_DB_CHARSET=$TENANT_DB_CHARSET \
|
||||
# Authentication
|
||||
JWT_SECRET=$JWT_SECRET \
|
||||
# Application
|
||||
BASE_URL=$BASE_URL \
|
||||
# Sign-up restriction
|
||||
SIGNUP_DISABLED=$SIGNUP_DISABLED \
|
||||
SIGNUP_ALLOWED_DOMAINS=$SIGNUP_ALLOWED_DOMAINS \
|
||||
SIGNUP_ALLOWED_EMAILS=$SIGNUP_ALLOWED_EMAILS
|
||||
|
||||
# New Relic config file.
|
||||
ENV NEW_RELIC_NO_CONFIG_FILE=true
|
||||
|
||||
# Create app directory.
|
||||
WORKDIR /app
|
||||
|
||||
RUN chown node:node /
|
||||
# Install pnpm
|
||||
RUN npm install -g pnpm@8.10.2
|
||||
|
||||
# Install pnpm
|
||||
RUN npm install -g pnpm
|
||||
# Install build dependencies
|
||||
RUN apk add --no-cache python3 build-base chromium
|
||||
|
||||
# Copy application dependency manifests to the container image.
|
||||
COPY --chown=node:node ./ ./
|
||||
|
||||
# Install application dependencies
|
||||
RUN apk update
|
||||
RUN apk add python3 build-base chromium
|
||||
|
||||
# Set PYHTON env
|
||||
# Set Python environment
|
||||
ENV PYTHON=/usr/bin/python3
|
||||
|
||||
# Install packages dependencies for production.
|
||||
RUN pnpm install
|
||||
# Copy package files for dependency installation
|
||||
COPY --chown=node:node package.json pnpm-lock.yaml pnpm-workspace.yaml lerna.json ./
|
||||
COPY --chown=node:node packages/server/package.json ./packages/server/
|
||||
COPY --chown=node:node shared/bigcapital-utils/package.json ./shared/bigcapital-utils/
|
||||
COPY --chown=node:node shared/pdf-templates/package.json ./shared/pdf-templates/
|
||||
COPY --chown=node:node shared/email-components/package.json ./shared/email-components/
|
||||
|
||||
# Install all dependencies (including devDependencies for build)
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# Copy source code
|
||||
COPY --chown=node:node ./packages/server ./packages/server
|
||||
COPY --chown=node:node ./shared/bigcapital-utils ./shared/bigcapital-utils
|
||||
COPY --chown=node:node ./shared/pdf-templates ./shared/pdf-templates
|
||||
COPY --chown=node:node ./shared/email-components ./shared/email-components
|
||||
|
||||
# # Creates a "dist" folder with the production build
|
||||
# Build NestJS application
|
||||
RUN pnpm run build:server --skip-nx-cache
|
||||
|
||||
CMD [ "node", "./packages/server/build/index.js" ]
|
||||
# Stage 2: Production
|
||||
FROM node:18.16.0-alpine AS production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install pnpm for production
|
||||
RUN npm install -g pnpm@8.10.2
|
||||
|
||||
# Create non-root user
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S nodejs -u 1001
|
||||
|
||||
# Install build dependencies for native modules (bcrypt, etc.)
|
||||
RUN apk add --no-cache python3 build-base
|
||||
|
||||
# Set Python environment
|
||||
ENV PYTHON=/usr/bin/python3
|
||||
|
||||
# Copy package files for production dependency installation
|
||||
COPY --chown=nodejs:nodejs package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
COPY --chown=nodejs:nodejs packages/server/package.json ./packages/server/
|
||||
COPY --chown=nodejs:nodejs shared/bigcapital-utils/package.json ./shared/bigcapital-utils/
|
||||
COPY --chown=nodejs:nodejs shared/pdf-templates/package.json ./shared/pdf-templates/
|
||||
COPY --chown=nodejs:nodejs shared/email-components/package.json ./shared/email-components/
|
||||
|
||||
# Copy .husky directory (needed for husky install command)
|
||||
COPY --chown=nodejs:nodejs .husky ./.husky
|
||||
|
||||
# Install only production dependencies
|
||||
# Install husky temporarily so prepare script can run, then remove it
|
||||
RUN pnpm add -D -w husky && \
|
||||
pnpm install --prod --frozen-lockfile && \
|
||||
pnpm remove -w husky && \
|
||||
# Remove build dependencies to reduce image size
|
||||
apk del python3 build-base
|
||||
|
||||
# Copy built application from builder stage
|
||||
COPY --from=builder --chown=nodejs:nodejs /app/packages/server/dist ./packages/server/dist
|
||||
|
||||
# Copy static assets (i18n, public, static directories)
|
||||
COPY --from=builder --chown=nodejs:nodejs /app/packages/server/src/i18n ./packages/server/dist/i18n
|
||||
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
|
||||
COPY --from=builder --chown=nodejs:nodejs /app/shared/email-components/dist ./shared/email-components/dist
|
||||
|
||||
# Set runtime environment variables (these should be provided at runtime via docker-compose or k8s)
|
||||
ENV NODE_ENV=production
|
||||
ENV NEW_RELIC_NO_CONFIG_FILE=true
|
||||
ENV PORT=3000
|
||||
|
||||
# Switch to non-root user
|
||||
USER nodejs
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
# Health check - uses /api/system_db ping endpoint
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
|
||||
CMD node -e "require('http').get('http://localhost:3000/api/system_db', (r) => {process.exit(r.statusCode >= 200 && r.statusCode < 300 ? 0 : 1)}).on('error', () => process.exit(1))"
|
||||
|
||||
# Start the application
|
||||
CMD [ "node", "packages/server/dist/main.js" ]
|
||||
|
||||
@@ -2,10 +2,23 @@
|
||||
"$schema": "https://json.schemastore.org/nest-cli",
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"entryFile": "main",
|
||||
"compilerOptions": {
|
||||
"deleteOutDir": true,
|
||||
"assets": [
|
||||
{ "include": "i18n/**/*", "watchAssets": true }
|
||||
{ "include": "i18n/**/*", "watchAssets": true },
|
||||
{ "include": "database/**/*", "exclude": "**/*.ts", "watchAssets": true }
|
||||
]
|
||||
},
|
||||
"projects": {
|
||||
"cli": {
|
||||
"type": "application",
|
||||
"root": "src",
|
||||
"entryFile": "cli",
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"tsConfigPath": "tsconfig.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
"@lemonsqueezy/lemonsqueezy.js": "^2.2.0",
|
||||
"@liaoliaots/nestjs-redis": "^10.0.0",
|
||||
"@nest-lab/throttler-storage-redis": "^1.1.0",
|
||||
"@bull-board/api": "^5.22.0",
|
||||
"@bull-board/express": "^5.22.0",
|
||||
"@bull-board/nestjs": "^5.22.0",
|
||||
"@nestjs/bull": "^10.2.1",
|
||||
"@nestjs/bullmq": "^10.2.2",
|
||||
"@nestjs/cache-manager": "^2.2.2",
|
||||
|
||||
8
packages/server/src/common/config/bull-board.ts
Normal file
8
packages/server/src/common/config/bull-board.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { registerAs } from '@nestjs/config';
|
||||
import { parseBoolean } from '@/utils/parse-boolean';
|
||||
|
||||
export default registerAs('bullBoard', () => ({
|
||||
enabled: parseBoolean<boolean>(process.env.BULL_BOARD_ENABLED, false),
|
||||
username: process.env.BULL_BOARD_USERNAME,
|
||||
password: process.env.BULL_BOARD_PASSWORD,
|
||||
}));
|
||||
@@ -17,6 +17,9 @@ import loops from './loops';
|
||||
import bankfeed from './bankfeed';
|
||||
import throttle from './throttle';
|
||||
import cloud from './cloud';
|
||||
import redis from './redis';
|
||||
import queue from './queue';
|
||||
import bullBoard from './bull-board';
|
||||
|
||||
export const config = [
|
||||
app,
|
||||
@@ -38,4 +41,7 @@ export const config = [
|
||||
loops,
|
||||
bankfeed,
|
||||
throttle,
|
||||
redis,
|
||||
queue,
|
||||
bullBoard,
|
||||
];
|
||||
|
||||
6
packages/server/src/common/config/queue.ts
Normal file
6
packages/server/src/common/config/queue.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { registerAs } from '@nestjs/config';
|
||||
|
||||
export default registerAs('queue', () => ({
|
||||
host: process.env.QUEUE_HOST || 'localhost',
|
||||
port: parseInt(process.env.QUEUE_PORT, 10) || 6379,
|
||||
}));
|
||||
59
packages/server/src/middleware/bull-board-auth.middleware.ts
Normal file
59
packages/server/src/middleware/bull-board-auth.middleware.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
|
||||
/**
|
||||
* Creates Express middleware for the Bull Board UI:
|
||||
* - When disabled: responds with 404.
|
||||
* - When enabled and username/password are set: enforces HTTP Basic Auth (401 if invalid).
|
||||
* - When enabled and credentials are not set: allows access (no auth).
|
||||
*/
|
||||
export function createBullBoardAuthMiddleware(
|
||||
enabled: boolean,
|
||||
username: string | undefined,
|
||||
password: string | undefined,
|
||||
): (req: Request, res: Response, next: NextFunction) => void {
|
||||
return (req: Request, res: Response, next: NextFunction) => {
|
||||
if (!enabled) {
|
||||
res.status(404).send('Not Found');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!username || !password) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith('Basic ')) {
|
||||
res.setHeader('WWW-Authenticate', 'Basic realm="Bull Board"');
|
||||
res.status(401).send('Authentication required');
|
||||
return;
|
||||
}
|
||||
|
||||
const base64Credentials = authHeader.slice(6);
|
||||
let decoded: string;
|
||||
try {
|
||||
decoded = Buffer.from(base64Credentials, 'base64').toString('utf8');
|
||||
} catch {
|
||||
res.setHeader('WWW-Authenticate', 'Basic realm="Bull Board"');
|
||||
res.status(401).send('Invalid credentials');
|
||||
return;
|
||||
}
|
||||
|
||||
const colonIndex = decoded.indexOf(':');
|
||||
if (colonIndex === -1) {
|
||||
res.setHeader('WWW-Authenticate', 'Basic realm="Bull Board"');
|
||||
res.status(401).send('Invalid credentials');
|
||||
return;
|
||||
}
|
||||
|
||||
const reqUsername = decoded.slice(0, colonIndex);
|
||||
const reqPassword = decoded.slice(colonIndex + 1);
|
||||
|
||||
if (reqUsername !== username || reqPassword !== password) {
|
||||
res.setHeader('WWW-Authenticate', 'Basic realm="Bull Board"');
|
||||
res.status(401).send('Invalid credentials');
|
||||
return;
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
@@ -33,6 +33,7 @@ export class AccountTransaction extends BaseModel {
|
||||
public readonly userId!: number;
|
||||
public readonly itemId!: number;
|
||||
public readonly projectId!: number;
|
||||
public readonly costable!: boolean;
|
||||
public readonly account: Account;
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,9 @@ import {
|
||||
I18nModule,
|
||||
QueryResolver,
|
||||
} from 'nestjs-i18n';
|
||||
import { BullBoardModule } from '@bull-board/nestjs';
|
||||
import { ExpressAdapter } from '@bull-board/express';
|
||||
import { createBullBoardAuthMiddleware } from '@/middleware/bull-board-auth.middleware';
|
||||
import { BullModule } from '@nestjs/bullmq';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
@@ -137,12 +140,30 @@ import { AppThrottleModule } from './AppThrottle.module';
|
||||
imports: [ConfigModule],
|
||||
useFactory: async (configService: ConfigService) => ({
|
||||
connection: {
|
||||
host: configService.get('QUEUE_HOST'),
|
||||
port: configService.get('QUEUE_PORT'),
|
||||
host: configService.get('queue.host'),
|
||||
port: configService.get('queue.port'),
|
||||
},
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
BullBoardModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
useFactory: (configService: ConfigService) => {
|
||||
const enabled = configService.get<boolean>('bullBoard.enabled');
|
||||
const username = configService.get<string>('bullBoard.username');
|
||||
const password = configService.get<string>('bullBoard.password');
|
||||
return {
|
||||
route: '/queues',
|
||||
adapter: ExpressAdapter,
|
||||
middleware: createBullBoardAuthMiddleware(
|
||||
enabled,
|
||||
username,
|
||||
password,
|
||||
),
|
||||
};
|
||||
},
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
ClsModule.forRoot({
|
||||
global: true,
|
||||
middleware: {
|
||||
@@ -158,8 +179,8 @@ import { AppThrottleModule } from './AppThrottle.module';
|
||||
imports: [ConfigModule],
|
||||
useFactory: (configService: ConfigService) => ({
|
||||
config: {
|
||||
host: configService.get('redis.host') || 'localhost',
|
||||
port: configService.get('redis.port') || 6379,
|
||||
host: configService.get('redis.host'),
|
||||
port: configService.get('redis.port'),
|
||||
},
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
|
||||
@@ -17,6 +17,8 @@ import { PassportModule } from '@nestjs/passport';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
import { JwtAuthGuard } from './guards/jwt.guard';
|
||||
import { AuthMailSubscriber } from './subscribers/AuthMail.subscriber';
|
||||
import { BullBoardModule } from '@bull-board/nestjs';
|
||||
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
|
||||
import { BullModule } from '@nestjs/bullmq';
|
||||
import {
|
||||
SendResetPasswordMailQueue,
|
||||
@@ -63,6 +65,14 @@ const models = [
|
||||
TenancyModule,
|
||||
BullModule.registerQueue({ name: SendResetPasswordMailQueue }),
|
||||
BullModule.registerQueue({ name: SendSignupVerificationMailQueue }),
|
||||
BullBoardModule.forFeature({
|
||||
name: SendResetPasswordMailQueue,
|
||||
adapter: BullMQAdapter,
|
||||
}),
|
||||
BullBoardModule.forFeature({
|
||||
name: SendSignupVerificationMailQueue,
|
||||
adapter: BullMQAdapter,
|
||||
}),
|
||||
],
|
||||
exports: [...models],
|
||||
providers: [
|
||||
@@ -98,4 +108,4 @@ const models = [
|
||||
AuthMailSubscriber,
|
||||
],
|
||||
})
|
||||
export class AuthModule { }
|
||||
export class AuthModule {}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { Processor, WorkerHost } from '@nestjs/bullmq';
|
||||
import { Scope } from '@nestjs/common';
|
||||
import {
|
||||
SendResetPasswordMailJob,
|
||||
SendResetPasswordMailQueue,
|
||||
} from '../Auth.constants';
|
||||
import { Process } from '@nestjs/bull';
|
||||
import { SendResetPasswordMailQueue } from '../Auth.constants';
|
||||
import { Job } from 'bullmq';
|
||||
import { AuthenticationMailMesssages } from '../AuthMailMessages.esrvice';
|
||||
import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
|
||||
@@ -23,7 +19,6 @@ export class SendResetPasswordMailProcessor extends WorkerHost {
|
||||
super();
|
||||
}
|
||||
|
||||
@Process(SendResetPasswordMailJob)
|
||||
async process(job: Job<SendResetPasswordMailJobPayload>) {
|
||||
try {
|
||||
await this.authMailMesssages.sendResetPasswordMail(
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { Scope } from '@nestjs/common';
|
||||
import { Job } from 'bullmq';
|
||||
import { Process } from '@nestjs/bull';
|
||||
import { Processor, WorkerHost } from '@nestjs/bullmq';
|
||||
import {
|
||||
SendSignupVerificationMailJob,
|
||||
SendSignupVerificationMailQueue,
|
||||
} from '../Auth.constants';
|
||||
import { SendSignupVerificationMailQueue } from '../Auth.constants';
|
||||
import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
|
||||
import { AuthenticationMailMesssages } from '../AuthMailMessages.esrvice';
|
||||
|
||||
@@ -21,7 +17,6 @@ export class SendSignupVerificationMailProcessor extends WorkerHost {
|
||||
super();
|
||||
}
|
||||
|
||||
@Process(SendSignupVerificationMailJob)
|
||||
async process(job: Job<SendSignupVerificationMailJobPayload>) {
|
||||
try {
|
||||
await this.authMailMesssages.sendSignupVerificationMail(
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { BullBoardModule } from '@bull-board/nestjs';
|
||||
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
|
||||
import { BullModule } from '@nestjs/bullmq';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { SocketModule } from '../Socket/Socket.module';
|
||||
@@ -33,6 +35,10 @@ const models = [RegisterTenancyModel(PlaidItem)];
|
||||
BankingCategorizeModule,
|
||||
BankingTransactionsModule,
|
||||
BullModule.registerQueue({ name: UpdateBankingPlaidTransitionsQueueJob }),
|
||||
BullBoardModule.forFeature({
|
||||
name: UpdateBankingPlaidTransitionsQueueJob,
|
||||
adapter: BullMQAdapter,
|
||||
}),
|
||||
...models,
|
||||
],
|
||||
providers: [
|
||||
@@ -51,4 +57,4 @@ const models = [RegisterTenancyModel(PlaidItem)];
|
||||
exports: [...models],
|
||||
controllers: [BankingPlaidController, BankingPlaidWebhooksController],
|
||||
})
|
||||
export class BankingPlaidModule { }
|
||||
export class BankingPlaidModule {}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
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';
|
||||
@@ -28,7 +26,6 @@ export class PlaidFetchTransactionsProcessor extends WorkerHost {
|
||||
/**
|
||||
* Triggers the function.
|
||||
*/
|
||||
@Process(UpdateBankingPlaidTransitionsJob)
|
||||
@UseCls()
|
||||
async process(job: Job<PlaidFetchTransitonsEventPayload>) {
|
||||
const { plaidItemId } = job.data;
|
||||
|
||||
@@ -10,6 +10,8 @@ import { BankingRecognizedTransactionsController } from './BankingRecognizedTran
|
||||
import { RecognizedTransactionsApplication } from './RecognizedTransactions.application';
|
||||
import { GetRecognizedTransactionsService } from './GetRecongizedTransactions';
|
||||
import { GetRecognizedTransactionService } from './queries/GetRecognizedTransaction.service';
|
||||
import { BullBoardModule } from '@bull-board/nestjs';
|
||||
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
|
||||
import { BullModule } from '@nestjs/bullmq';
|
||||
import { RecognizeUncategorizedTransactionsQueue } from './_types';
|
||||
import { RegonizeTransactionsPrcessor } from './jobs/RecognizeTransactionsJob';
|
||||
@@ -25,6 +27,10 @@ const models = [RegisterTenancyModel(RecognizedBankTransaction)];
|
||||
BullModule.registerQueue({
|
||||
name: RecognizeUncategorizedTransactionsQueue,
|
||||
}),
|
||||
BullBoardModule.forFeature({
|
||||
name: RecognizeUncategorizedTransactionsQueue,
|
||||
adapter: BullMQAdapter,
|
||||
}),
|
||||
...models,
|
||||
],
|
||||
providers: [
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
RecognizeUncategorizedTransactionsJobPayload,
|
||||
RecognizeUncategorizedTransactionsQueue,
|
||||
} from '../_types';
|
||||
import { Process } from '@nestjs/bull';
|
||||
|
||||
@Processor({
|
||||
name: RecognizeUncategorizedTransactionsQueue,
|
||||
@@ -28,7 +27,6 @@ export class RegonizeTransactionsPrcessor extends WorkerHost {
|
||||
/**
|
||||
* Triggers sending invoice mail.
|
||||
*/
|
||||
@Process(RecognizeUncategorizedTransactionsQueue)
|
||||
@UseCls()
|
||||
async process(job: Job<RecognizeUncategorizedTransactionsJobPayload>) {
|
||||
const { ruleId, transactionsCriteria } = job.data;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
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 {
|
||||
@@ -89,6 +91,14 @@ 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,
|
||||
|
||||
@@ -30,6 +30,7 @@ export class BillTransformer extends Transformer {
|
||||
'taxes',
|
||||
'entries',
|
||||
'attachments',
|
||||
'branch',
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
@@ -31,6 +31,12 @@ 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({
|
||||
@@ -66,7 +72,13 @@ import { FeaturesModule } from '../Features/Features.module';
|
||||
ValidateBranchExistance,
|
||||
ManualJournalBranchesValidator,
|
||||
CashflowTransactionsActivateBranches,
|
||||
ExpensesActivateBranches
|
||||
ExpensesActivateBranches,
|
||||
BillActivateBranches,
|
||||
VendorCreditActivateBranches,
|
||||
BillPaymentsActivateBranches,
|
||||
BillBranchesActivateSubscriber,
|
||||
VendorCreditBranchesActivateSubscriber,
|
||||
PaymentMadeActivateBranchesSubscriber
|
||||
],
|
||||
exports: [
|
||||
BranchesSettingsService,
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { Bill } from '@/modules/Bills/models/Bill';
|
||||
|
||||
@Injectable()
|
||||
export class BillActivateBranches {
|
||||
constructor(private readonly billModel: TenantModelProxy<typeof Bill>) {}
|
||||
constructor(
|
||||
@Inject(Bill.name)
|
||||
private readonly billModel: TenantModelProxy<typeof Bill>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Updates all bills transactions with the primary branch.
|
||||
@@ -17,7 +20,7 @@ export class BillActivateBranches {
|
||||
primaryBranchId: number,
|
||||
trx?: Knex.Transaction,
|
||||
) => {
|
||||
// Updates the sale invoice with primary branch.
|
||||
await Bill.query(trx).update({ branchId: primaryBranchId });
|
||||
// Updates the bills with primary branch.
|
||||
await this.billModel().query(trx).update({ branchId: primaryBranchId });
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
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>,
|
||||
) {}
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Inject, 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>,
|
||||
) {}
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ export abstract class BaseCommand extends CommandRunner {
|
||||
},
|
||||
migrations: {
|
||||
directory: this.configService.get('systemDatabase.migrationDir'),
|
||||
loadExtensions: ['.js'],
|
||||
},
|
||||
seeds: {
|
||||
directory: this.configService.get('systemDatabase.seedsDir'),
|
||||
@@ -43,6 +44,7 @@ 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',
|
||||
|
||||
@@ -24,14 +24,14 @@ export class BalanceSheetQueryDto extends FinancialSheetBranchesQueryDto {
|
||||
displayColumnsType: 'total' | 'date_periods' = 'total';
|
||||
|
||||
@ApiProperty({
|
||||
enum: ['day', 'month', 'year'],
|
||||
enum: ['day', 'month', 'year', 'quarter'],
|
||||
default: 'year',
|
||||
description: 'Time period for column display',
|
||||
})
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@IsEnum(['day', 'month', 'year'])
|
||||
displayColumnsBy: 'day' | 'month' | 'year' = 'year';
|
||||
@IsEnum(['day', 'month', 'year', 'quarter'])
|
||||
displayColumnsBy: 'day' | 'month' | 'year' | 'quarter' = 'year';
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Start date for the balance sheet period',
|
||||
|
||||
@@ -34,13 +34,13 @@ export class CashFlowStatementQueryDto extends FinancialSheetBranchesQueryDto {
|
||||
@ApiProperty({
|
||||
description: 'Display columns by time period',
|
||||
required: false,
|
||||
enum: ['day', 'month', 'year'],
|
||||
enum: ['day', 'month', 'year', 'quarter'],
|
||||
default: 'year',
|
||||
})
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@IsEnum(['day', 'month', 'year'])
|
||||
displayColumnsBy: 'day' | 'month' | 'year' = 'year';
|
||||
@IsEnum(['day', 'month', 'year', 'quarter'])
|
||||
displayColumnsBy: 'day' | 'month' | 'year' | 'quarter' = 'year';
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Type of column display',
|
||||
|
||||
@@ -64,10 +64,10 @@ export class ProfitLossSheetQueryDto extends FinancialSheetBranchesQueryDto {
|
||||
displayColumnsType: 'total' | 'date_periods';
|
||||
|
||||
@IsString()
|
||||
@IsEnum(['day', 'month', 'year'])
|
||||
@IsEnum(['day', 'month', 'year', 'quarter'])
|
||||
@IsOptional()
|
||||
@ApiProperty({ description: 'How to display columns' })
|
||||
displayColumnsBy: 'day' | 'month' | 'year' = 'year';
|
||||
displayColumnsBy: 'day' | 'month' | 'year' | 'quarter' = 'year';
|
||||
|
||||
@Transform(({ value }) => parseBoolean(value, false))
|
||||
@IsBoolean()
|
||||
|
||||
@@ -16,6 +16,8 @@ import {
|
||||
ComputeItemCostQueue,
|
||||
WriteInventoryTransactionsGLEntriesQueue,
|
||||
} from './types/InventoryCost.types';
|
||||
import { BullBoardModule } from '@bull-board/nestjs';
|
||||
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
|
||||
import { BullModule } from '@nestjs/bullmq';
|
||||
import { InventoryAverageCostMethodService } from './commands/InventoryAverageCostMethod.service';
|
||||
import { InventoryItemCostService } from './commands/InventoryCosts.service';
|
||||
@@ -39,6 +41,14 @@ const models = [
|
||||
BullModule.registerQueue({
|
||||
name: WriteInventoryTransactionsGLEntriesQueue,
|
||||
}),
|
||||
BullBoardModule.forFeature({
|
||||
name: ComputeItemCostQueue,
|
||||
adapter: BullMQAdapter,
|
||||
}),
|
||||
BullBoardModule.forFeature({
|
||||
name: WriteInventoryTransactionsGLEntriesQueue,
|
||||
adapter: BullMQAdapter,
|
||||
}),
|
||||
forwardRef(() => SaleInvoicesModule),
|
||||
ImportModule,
|
||||
],
|
||||
@@ -56,7 +66,7 @@ const models = [
|
||||
InventoryItemCostService,
|
||||
InventoryItemOpeningAvgCostService,
|
||||
InventoryCostSubscriber,
|
||||
GetItemsInventoryValuationListService
|
||||
GetItemsInventoryValuationListService,
|
||||
],
|
||||
exports: [
|
||||
...models,
|
||||
@@ -64,6 +74,6 @@ const models = [
|
||||
InventoryItemCostService,
|
||||
InventoryComputeCostService,
|
||||
],
|
||||
controllers: [InventoryCostController]
|
||||
controllers: [InventoryCostController],
|
||||
})
|
||||
export class InventoryCostModule {}
|
||||
|
||||
@@ -7,11 +7,7 @@ import * as moment from 'moment';
|
||||
import { TenantJobPayload } from '@/interfaces/Tenant';
|
||||
import { InventoryComputeCostService } from '../commands/InventoryComputeCost.service';
|
||||
import { events } from '@/common/events/events';
|
||||
import {
|
||||
ComputeItemCostQueue,
|
||||
ComputeItemCostQueueJob,
|
||||
} from '../types/InventoryCost.types';
|
||||
import { Process } from '@nestjs/bull';
|
||||
import { ComputeItemCostQueue } from '../types/InventoryCost.types';
|
||||
|
||||
interface ComputeItemCostJobPayload extends TenantJobPayload {
|
||||
itemId: number;
|
||||
@@ -39,7 +35,6 @@ 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;
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import { Process } from '@nestjs/bull';
|
||||
import {
|
||||
WriteInventoryTransactionsGLEntriesQueue,
|
||||
WriteInventoryTransactionsGLEntriesQueueJob,
|
||||
} from '../types/InventoryCost.types';
|
||||
import { WriteInventoryTransactionsGLEntriesQueue } from '../types/InventoryCost.types';
|
||||
import { Processor, WorkerHost } from '@nestjs/bullmq';
|
||||
import { Scope } from '@nestjs/common';
|
||||
|
||||
@@ -15,6 +11,5 @@ export class WriteInventoryTransactionsGLEntriesProcessor extends WorkerHost {
|
||||
super();
|
||||
}
|
||||
|
||||
@Process(WriteInventoryTransactionsGLEntriesQueueJob)
|
||||
async process() {}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
BulkDeleteItemsDto,
|
||||
ValidateBulkDeleteItemsResponseDto,
|
||||
} from './dtos/BulkDeleteItems.dto';
|
||||
import { ItemApiErrorResponseDto } from './dtos/ItemErrorResponse.dto';
|
||||
|
||||
@Controller('/items')
|
||||
@ApiTags('Items')
|
||||
@@ -45,6 +46,7 @@ import {
|
||||
@ApiExtraModels(ItemEstimatesResponseDto)
|
||||
@ApiExtraModels(ItemReceiptsResponseDto)
|
||||
@ApiExtraModels(ValidateBulkDeleteItemsResponseDto)
|
||||
@ApiExtraModels(ItemApiErrorResponseDto)
|
||||
@ApiCommonHeaders()
|
||||
export class ItemsController extends TenantController {
|
||||
constructor(private readonly itemsApplication: ItemsApplicationService) {
|
||||
@@ -147,6 +149,13 @@ 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',
|
||||
@@ -204,6 +213,13 @@ 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,
|
||||
@@ -219,6 +235,13 @@ 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',
|
||||
|
||||
112
packages/server/src/modules/Items/dtos/ItemErrorResponse.dto.ts
Normal file
112
packages/server/src/modules/Items/dtos/ItemErrorResponse.dto.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
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[];
|
||||
}
|
||||
@@ -35,7 +35,7 @@ export const transformLedgerEntryToTransaction = (
|
||||
itemId: entry.itemId,
|
||||
projectId: entry.projectId,
|
||||
|
||||
// costable: entry.costable,
|
||||
costable: entry.costable,
|
||||
|
||||
taxRateId: entry.taxRateId,
|
||||
taxRate: entry.taxRate,
|
||||
|
||||
@@ -3,6 +3,8 @@ import { GetCurrentOrganizationService } from './queries/GetCurrentOrganization.
|
||||
import { BuildOrganizationService } from './commands/BuildOrganization.service';
|
||||
import { UpdateOrganizationService } from './commands/UpdateOrganization.service';
|
||||
import { OrganizationController } from './Organization.controller';
|
||||
import { BullBoardModule } from '@bull-board/nestjs';
|
||||
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
|
||||
import { BullModule } from '@nestjs/bullmq';
|
||||
import { OrganizationBuildQueue } from './Organization.types';
|
||||
import { OrganizationBuildProcessor } from './processors/OrganizationBuild.processor';
|
||||
@@ -25,10 +27,14 @@ import { GetBuildOrganizationBuildJob } from './commands/GetBuildOrganizationJob
|
||||
OrganizationBaseCurrencyLocking,
|
||||
SyncSystemUserToTenantService,
|
||||
SyncSystemUserToTenantSubscriber,
|
||||
GetBuildOrganizationBuildJob
|
||||
GetBuildOrganizationBuildJob,
|
||||
],
|
||||
imports: [
|
||||
BullModule.registerQueue({ name: OrganizationBuildQueue }),
|
||||
BullBoardModule.forFeature({
|
||||
name: OrganizationBuildQueue,
|
||||
adapter: BullMQAdapter,
|
||||
}),
|
||||
TenantDBManagerModule,
|
||||
],
|
||||
controllers: [OrganizationController],
|
||||
|
||||
@@ -2,10 +2,8 @@ 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';
|
||||
@@ -22,7 +20,6 @@ export class OrganizationBuildProcessor extends WorkerHost {
|
||||
super();
|
||||
}
|
||||
|
||||
@Process(OrganizationBuildQueueJob)
|
||||
@UseCls()
|
||||
async process(job: Job<OrganizationBuildQueueJobPayload>) {
|
||||
console.log('Processing organization build job:', job.id);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { BullBoardModule } from '@bull-board/nestjs';
|
||||
import { BullAdapter } from '@bull-board/api/bullAdapter';
|
||||
import { BullModule } from '@nestjs/bull';
|
||||
import { PaymentReceivesController } from './PaymentsReceived.controller';
|
||||
import { PaymentReceivesApplication } from './PaymentReceived.application';
|
||||
@@ -95,6 +97,10 @@ import { ValidateBulkDeletePaymentReceivedService } from './ValidateBulkDeletePa
|
||||
DynamicListModule,
|
||||
MailModule,
|
||||
BullModule.registerQueue({ name: SEND_PAYMENT_RECEIVED_MAIL_QUEUE }),
|
||||
BullBoardModule.forFeature({
|
||||
name: SEND_PAYMENT_RECEIVED_MAIL_QUEUE,
|
||||
adapter: BullAdapter,
|
||||
}),
|
||||
],
|
||||
})
|
||||
export class PaymentsReceivedModule {}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { BullBoardModule } from '@bull-board/nestjs';
|
||||
import { BullAdapter } from '@bull-board/api/bullAdapter';
|
||||
import { BullModule } from '@nestjs/bull';
|
||||
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||
import { TenancyDatabaseModule } from '../Tenancy/TenancyDB/TenancyDB.module';
|
||||
@@ -54,6 +56,10 @@ import { SendSaleEstimateMailProcess } from './processes/SendSaleEstimateMail.pr
|
||||
TemplateInjectableModule,
|
||||
PdfTemplatesModule,
|
||||
BullModule.registerQueue({ name: SendSaleEstimateMailQueue }),
|
||||
BullBoardModule.forFeature({
|
||||
name: SendSaleEstimateMailQueue,
|
||||
adapter: BullAdapter,
|
||||
}),
|
||||
],
|
||||
controllers: [SaleEstimatesController],
|
||||
providers: [
|
||||
@@ -99,4 +105,4 @@ import { SendSaleEstimateMailProcess } from './processes/SendSaleEstimateMail.pr
|
||||
GetSaleEstimateMailTemplateService,
|
||||
],
|
||||
})
|
||||
export class SaleEstimatesModule { }
|
||||
export class SaleEstimatesModule {}
|
||||
|
||||
@@ -45,6 +45,8 @@ import { SendSaleInvoiceMailCommon } from './commands/SendInvoiceInvoiceMailComm
|
||||
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
|
||||
import { MailNotificationModule } from '../MailNotification/MailNotification.module';
|
||||
import { SendSaleInvoiceMailProcessor } from './processors/SendSaleInvoiceMail.processor';
|
||||
import { BullBoardModule } from '@bull-board/nestjs';
|
||||
import { BullAdapter } from '@bull-board/api/bullAdapter';
|
||||
import { BullModule } from '@nestjs/bull';
|
||||
import { SendSaleInvoiceQueue } from './constants';
|
||||
import { InvoicePaymentIntegrationSubscriber } from './subscribers/InvoicePaymentIntegrationSubscriber';
|
||||
@@ -81,6 +83,10 @@ import { ValidateBulkDeleteSaleInvoicesService } from './ValidateBulkDeleteSaleI
|
||||
forwardRef(() => PaymentLinksModule),
|
||||
DynamicListModule,
|
||||
BullModule.registerQueue({ name: SendSaleInvoiceQueue }),
|
||||
BullBoardModule.forFeature({
|
||||
name: SendSaleInvoiceQueue,
|
||||
adapter: BullAdapter,
|
||||
}),
|
||||
],
|
||||
controllers: [SaleInvoicesController],
|
||||
providers: [
|
||||
@@ -139,4 +145,4 @@ import { ValidateBulkDeleteSaleInvoicesService } from './ValidateBulkDeleteSaleI
|
||||
SaleInvoicesImportable,
|
||||
],
|
||||
})
|
||||
export class SaleInvoicesModule { }
|
||||
export class SaleInvoicesModule {}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
import * as R from 'ramda';
|
||||
import { Knex } from 'knex';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||
import { InventoryCostLotTracker } from '../InventoryCost/models/InventoryCostLotTracker';
|
||||
import { LedgerStorageService } from '../Ledger/LedgerStorage.service';
|
||||
import { groupInventoryTransactionsByTypeId } from '../InventoryCost/utils';
|
||||
import { Ledger } from '../Ledger/Ledger';
|
||||
import { AccountNormal } from '@/interfaces/Account';
|
||||
import { ILedgerEntry } from '../Ledger/types/Ledger.types';
|
||||
import { increment } from '@/utils/increment';
|
||||
|
||||
@Injectable()
|
||||
export class SaleReceiptCostGLEntries {
|
||||
constructor(
|
||||
private readonly ledgerStorage: LedgerStorageService,
|
||||
|
||||
@Inject(InventoryCostLotTracker.name)
|
||||
private readonly inventoryCostLotTracker: TenantModelProxy<
|
||||
typeof InventoryCostLotTracker
|
||||
>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Writes journal entries from sales receipts.
|
||||
* @param {Date} startingDate - Starting date.
|
||||
* @param {Knex.Transaction} trx - Transaction.
|
||||
*/
|
||||
public writeInventoryCostJournalEntries = async (
|
||||
startingDate: Date,
|
||||
trx?: Knex.Transaction,
|
||||
): Promise<void> => {
|
||||
const inventoryCostLotTrans = await this.inventoryCostLotTracker()
|
||||
.query()
|
||||
.where('direction', 'OUT')
|
||||
.where('transaction_type', 'SaleReceipt')
|
||||
.where('cost', '>', 0)
|
||||
.modify('filterDateRange', startingDate)
|
||||
.orderBy('date', 'ASC')
|
||||
.withGraphFetched('receipt')
|
||||
.withGraphFetched('item')
|
||||
.withGraphFetched('itemEntry');
|
||||
|
||||
const ledger = this.getInventoryCostLotsLedger(inventoryCostLotTrans);
|
||||
|
||||
await this.ledgerStorage.commit(ledger, trx);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the inventory cost lots ledger.
|
||||
*/
|
||||
private getInventoryCostLotsLedger = (
|
||||
inventoryCostLots: InventoryCostLotTracker[],
|
||||
) => {
|
||||
const inventoryTransactions =
|
||||
groupInventoryTransactionsByTypeId(inventoryCostLots);
|
||||
|
||||
const entries = inventoryTransactions
|
||||
.map(this.getSaleReceiptCostGLEntries)
|
||||
.flat();
|
||||
return new Ledger(entries);
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds the common GL entry fields for a sale receipt cost.
|
||||
*/
|
||||
private getReceiptCostGLCommonEntry = (
|
||||
inventoryCostLot: InventoryCostLotTracker,
|
||||
) => {
|
||||
return {
|
||||
currencyCode: inventoryCostLot.receipt.currencyCode,
|
||||
exchangeRate: inventoryCostLot.receipt.exchangeRate,
|
||||
|
||||
transactionType: inventoryCostLot.transactionType,
|
||||
transactionId: inventoryCostLot.transactionId,
|
||||
|
||||
transactionNumber: inventoryCostLot.receipt.receiptNumber,
|
||||
referenceNumber: inventoryCostLot.receipt.referenceNo,
|
||||
|
||||
date: inventoryCostLot.date,
|
||||
indexGroup: 20,
|
||||
costable: true,
|
||||
createdAt: inventoryCostLot.createdAt,
|
||||
|
||||
debit: 0,
|
||||
credit: 0,
|
||||
|
||||
branchId: inventoryCostLot.receipt.branchId,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the inventory cost GL entry for a single lot.
|
||||
*/
|
||||
private getInventoryCostGLEntry = R.curry(
|
||||
(
|
||||
getIndexIncrement: () => number,
|
||||
inventoryCostLot: InventoryCostLotTracker,
|
||||
): ILedgerEntry[] => {
|
||||
const commonEntry = this.getReceiptCostGLCommonEntry(inventoryCostLot);
|
||||
const costAccountId =
|
||||
inventoryCostLot.costAccountId || inventoryCostLot.item.costAccountId;
|
||||
|
||||
const description = inventoryCostLot.itemEntry?.description || null;
|
||||
|
||||
const costEntry = {
|
||||
...commonEntry,
|
||||
debit: inventoryCostLot.cost,
|
||||
accountId: costAccountId,
|
||||
accountNormal: AccountNormal.DEBIT,
|
||||
itemId: inventoryCostLot.itemId,
|
||||
note: description,
|
||||
index: getIndexIncrement(),
|
||||
};
|
||||
|
||||
const inventoryEntry = {
|
||||
...commonEntry,
|
||||
credit: inventoryCostLot.cost,
|
||||
accountId: inventoryCostLot.item.inventoryAccountId,
|
||||
accountNormal: AccountNormal.DEBIT,
|
||||
itemId: inventoryCostLot.itemId,
|
||||
note: description,
|
||||
index: getIndexIncrement(),
|
||||
};
|
||||
return [costEntry, inventoryEntry];
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Builds GL entries for a group of sale receipt cost lots.
|
||||
* - Cost of goods sold -> Debit
|
||||
* - Inventory assets -> Credit
|
||||
*/
|
||||
public getSaleReceiptCostGLEntries = (
|
||||
inventoryCostLots: InventoryCostLotTracker[],
|
||||
): ILedgerEntry[] => {
|
||||
const getIndexIncrement = increment(0);
|
||||
const getInventoryLotEntry =
|
||||
this.getInventoryCostGLEntry(getIndexIncrement);
|
||||
|
||||
return inventoryCostLots.map((t) => getInventoryLotEntry(t)).flat();
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { BullBoardModule } from '@bull-board/nestjs';
|
||||
import { BullAdapter } from '@bull-board/api/bullAdapter';
|
||||
import { BullModule } from '@nestjs/bull';
|
||||
import { SaleReceiptApplication } from './SaleReceiptApplication.service';
|
||||
import { CreateSaleReceipt } from './commands/CreateSaleReceipt.service';
|
||||
@@ -40,6 +42,8 @@ import { SaleReceiptsImportable } from './commands/SaleReceiptsImportable';
|
||||
import { GetSaleReceiptMailStateService } from './queries/GetSaleReceiptMailState.service';
|
||||
import { GetSaleReceiptMailTemplateService } from './queries/GetSaleReceiptMailTemplate.service';
|
||||
import { SaleReceiptAutoIncrementSubscriber } from './subscribers/SaleReceiptAutoIncrementSubscriber';
|
||||
import { SaleReceiptCostGLEntriesSubscriber } from './subscribers/SaleReceiptCostGLEntriesSubscriber';
|
||||
import { SaleReceiptCostGLEntries } from './SaleReceiptCostGLEntries';
|
||||
import { BulkDeleteSaleReceiptsService } from './BulkDeleteSaleReceipts.service';
|
||||
import { ValidateBulkDeleteSaleReceiptsService } from './ValidateBulkDeleteSaleReceipts.service';
|
||||
|
||||
@@ -60,6 +64,10 @@ import { ValidateBulkDeleteSaleReceiptsService } from './ValidateBulkDeleteSaleR
|
||||
MailModule,
|
||||
MailNotificationModule,
|
||||
BullModule.registerQueue({ name: SendSaleReceiptMailQueue }),
|
||||
BullBoardModule.forFeature({
|
||||
name: SendSaleReceiptMailQueue,
|
||||
adapter: BullAdapter,
|
||||
}),
|
||||
],
|
||||
providers: [
|
||||
TenancyContext,
|
||||
@@ -87,8 +95,10 @@ import { ValidateBulkDeleteSaleReceiptsService } from './ValidateBulkDeleteSaleR
|
||||
GetSaleReceiptMailStateService,
|
||||
GetSaleReceiptMailTemplateService,
|
||||
SaleReceiptAutoIncrementSubscriber,
|
||||
SaleReceiptCostGLEntries,
|
||||
SaleReceiptCostGLEntriesSubscriber,
|
||||
BulkDeleteSaleReceiptsService,
|
||||
ValidateBulkDeleteSaleReceiptsService,
|
||||
],
|
||||
})
|
||||
export class SaleReceiptsModule { }
|
||||
export class SaleReceiptsModule {}
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
// import { Service, Inject } from 'typedi';
|
||||
// import * as R from 'ramda';
|
||||
// import { Knex } from 'knex';
|
||||
// import { AccountNormal, IInventoryLotCost, ILedgerEntry } from '@/interfaces';
|
||||
// import { increment } from 'utils';
|
||||
// import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
// import Ledger from '@/services/Accounting/Ledger';
|
||||
// import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||
// import { groupInventoryTransactionsByTypeId } from '../../Inventory/utils';
|
||||
|
||||
// @Service()
|
||||
// export class SaleReceiptCostGLEntries {
|
||||
// @Inject()
|
||||
// private tenancy: HasTenancyService;
|
||||
|
||||
// @Inject()
|
||||
// private ledgerStorage: LedgerStorageService;
|
||||
|
||||
// /**
|
||||
// * Writes journal entries from sales invoices.
|
||||
// * @param {number} tenantId - The tenant id.
|
||||
// * @param {Date} startingDate - Starting date.
|
||||
// * @param {boolean} override
|
||||
// */
|
||||
// public writeInventoryCostJournalEntries = async (
|
||||
// tenantId: number,
|
||||
// startingDate: Date,
|
||||
// trx?: Knex.Transaction
|
||||
// ): Promise<void> => {
|
||||
// const { InventoryCostLotTracker } = this.tenancy.models(tenantId);
|
||||
|
||||
// const inventoryCostLotTrans = await InventoryCostLotTracker.query()
|
||||
// .where('direction', 'OUT')
|
||||
// .where('transaction_type', 'SaleReceipt')
|
||||
// .where('cost', '>', 0)
|
||||
// .modify('filterDateRange', startingDate)
|
||||
// .orderBy('date', 'ASC')
|
||||
// .withGraphFetched('receipt')
|
||||
// .withGraphFetched('item');
|
||||
|
||||
// const ledger = this.getInventoryCostLotsLedger(inventoryCostLotTrans);
|
||||
|
||||
// // Commit the ledger to the storage.
|
||||
// await this.ledgerStorage.commit(tenantId, ledger, trx);
|
||||
// };
|
||||
|
||||
// /**
|
||||
// * Retrieves the inventory cost lots ledger.
|
||||
// * @param {} inventoryCostLots
|
||||
// * @returns {Ledger}
|
||||
// */
|
||||
// private getInventoryCostLotsLedger = (
|
||||
// inventoryCostLots: IInventoryLotCost[]
|
||||
// ) => {
|
||||
// // Groups the inventory cost lots transactions.
|
||||
// const inventoryTransactions =
|
||||
// groupInventoryTransactionsByTypeId(inventoryCostLots);
|
||||
|
||||
// //
|
||||
// const entries = inventoryTransactions
|
||||
// .map(this.getSaleInvoiceCostGLEntries)
|
||||
// .flat();
|
||||
|
||||
// return new Ledger(entries);
|
||||
// };
|
||||
|
||||
// /**
|
||||
// *
|
||||
// * @param {IInventoryLotCost} inventoryCostLot
|
||||
// * @returns {}
|
||||
// */
|
||||
// private getInvoiceCostGLCommonEntry = (
|
||||
// inventoryCostLot: IInventoryLotCost
|
||||
// ) => {
|
||||
// return {
|
||||
// currencyCode: inventoryCostLot.receipt.currencyCode,
|
||||
// exchangeRate: inventoryCostLot.receipt.exchangeRate,
|
||||
|
||||
// transactionType: inventoryCostLot.transactionType,
|
||||
// transactionId: inventoryCostLot.transactionId,
|
||||
|
||||
// date: inventoryCostLot.date,
|
||||
// indexGroup: 20,
|
||||
// costable: true,
|
||||
// createdAt: inventoryCostLot.createdAt,
|
||||
|
||||
// debit: 0,
|
||||
// credit: 0,
|
||||
|
||||
// branchId: inventoryCostLot.receipt.branchId,
|
||||
// };
|
||||
// };
|
||||
|
||||
// /**
|
||||
// * Retrieves the inventory cost GL entry.
|
||||
// * @param {IInventoryLotCost} inventoryLotCost
|
||||
// * @returns {ILedgerEntry[]}
|
||||
// */
|
||||
// private getInventoryCostGLEntry = R.curry(
|
||||
// (
|
||||
// getIndexIncrement,
|
||||
// inventoryCostLot: IInventoryLotCost
|
||||
// ): ILedgerEntry[] => {
|
||||
// const commonEntry = this.getInvoiceCostGLCommonEntry(inventoryCostLot);
|
||||
// const costAccountId =
|
||||
// inventoryCostLot.costAccountId || inventoryCostLot.item.costAccountId;
|
||||
|
||||
// // XXX Debit - Cost account.
|
||||
// const costEntry = {
|
||||
// ...commonEntry,
|
||||
// debit: inventoryCostLot.cost,
|
||||
// accountId: costAccountId,
|
||||
// accountNormal: AccountNormal.DEBIT,
|
||||
// itemId: inventoryCostLot.itemId,
|
||||
// index: getIndexIncrement(),
|
||||
// };
|
||||
// // XXX Credit - Inventory account.
|
||||
// const inventoryEntry = {
|
||||
// ...commonEntry,
|
||||
// credit: inventoryCostLot.cost,
|
||||
// accountId: inventoryCostLot.item.inventoryAccountId,
|
||||
// accountNormal: AccountNormal.DEBIT,
|
||||
// itemId: inventoryCostLot.itemId,
|
||||
// index: getIndexIncrement(),
|
||||
// };
|
||||
// return [costEntry, inventoryEntry];
|
||||
// }
|
||||
// );
|
||||
|
||||
// /**
|
||||
// * Writes journal entries for given sale invoice.
|
||||
// * -------
|
||||
// * - Cost of goods sold -> Debit -> YYYY
|
||||
// * - Inventory assets -> Credit -> YYYY
|
||||
// * --------
|
||||
// * @param {ISaleInvoice} saleInvoice
|
||||
// * @param {JournalPoster} journal
|
||||
// */
|
||||
// public getSaleInvoiceCostGLEntries = (
|
||||
// inventoryCostLots: IInventoryLotCost[]
|
||||
// ): ILedgerEntry[] => {
|
||||
// const getIndexIncrement = increment(0);
|
||||
// const getInventoryLotEntry =
|
||||
// this.getInventoryCostGLEntry(getIndexIncrement);
|
||||
|
||||
// return inventoryCostLots.map(getInventoryLotEntry).flat();
|
||||
// };
|
||||
// }
|
||||
@@ -1,36 +1,26 @@
|
||||
// import { Inject, Service } from 'typedi';
|
||||
// import events from '@/subscribers/events';
|
||||
// import { IInventoryCostLotsGLEntriesWriteEvent } from '@/interfaces';
|
||||
// import { SaleReceiptCostGLEntries } from '../SaleReceiptCostGLEntries';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { events } from '@/common/events/events';
|
||||
import { IInventoryCostLotsGLEntriesWriteEvent } from '@/modules/InventoryCost/types/InventoryCost.types';
|
||||
import { SaleReceiptCostGLEntries } from '../SaleReceiptCostGLEntries';
|
||||
|
||||
// @Service()
|
||||
// export class SaleReceiptCostGLEntriesSubscriber {
|
||||
// @Inject()
|
||||
// private saleReceiptCostEntries: SaleReceiptCostGLEntries;
|
||||
@Injectable()
|
||||
export class SaleReceiptCostGLEntriesSubscriber {
|
||||
constructor(
|
||||
private readonly saleReceiptCostEntries: SaleReceiptCostGLEntries,
|
||||
) {}
|
||||
|
||||
// /**
|
||||
// * Attaches events.
|
||||
// */
|
||||
// public attach(bus) {
|
||||
// bus.subscribe(
|
||||
// events.inventory.onCostLotsGLEntriesWrite,
|
||||
// this.writeJournalEntriesOnceWriteoffCreate
|
||||
// );
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Writes the receipts cost GL entries once the inventory cost lots be written.
|
||||
// * @param {IInventoryCostLotsGLEntriesWriteEvent}
|
||||
// */
|
||||
// private writeJournalEntriesOnceWriteoffCreate = async ({
|
||||
// trx,
|
||||
// startingDate,
|
||||
// tenantId,
|
||||
// }: IInventoryCostLotsGLEntriesWriteEvent) => {
|
||||
// await this.saleReceiptCostEntries.writeInventoryCostJournalEntries(
|
||||
// tenantId,
|
||||
// startingDate,
|
||||
// trx
|
||||
// );
|
||||
// };
|
||||
// }
|
||||
/**
|
||||
* Writes the receipts cost GL entries once the inventory cost lots are written.
|
||||
*/
|
||||
@OnEvent(events.inventory.onCostLotsGLEntriesWrite)
|
||||
async writeReceiptsCostEntriesOnCostLotsWritten({
|
||||
trx,
|
||||
startingDate,
|
||||
}: IInventoryCostLotsGLEntriesWriteEvent) {
|
||||
await this.saleReceiptCostEntries.writeInventoryCostJournalEntries(
|
||||
startingDate,
|
||||
trx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ const providers = [
|
||||
},
|
||||
migrations: {
|
||||
directory: configService.get('systemDatabase.migrationDir'),
|
||||
loadExtensions: ['.js'],
|
||||
},
|
||||
seeds: {
|
||||
directory: configService.get('systemDatabase.seedsDir'),
|
||||
|
||||
@@ -33,6 +33,7 @@ export const TenancyDatabaseProxyProvider = ClsModule.forFeatureAsync({
|
||||
},
|
||||
migrations: {
|
||||
directory: configService.get('tenantDatabase.migrationsDir'),
|
||||
loadExtensions: ['.js'],
|
||||
},
|
||||
seeds: {
|
||||
directory: configService.get('tenantDatabase.seedsDir'),
|
||||
|
||||
@@ -1,27 +1,47 @@
|
||||
FROM node:18.16.0-alpine as build
|
||||
|
||||
USER root
|
||||
# Stage 1: Build
|
||||
FROM node:18.16.0-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy application dependency manifests to the container image.
|
||||
COPY . .
|
||||
# Install pnpm
|
||||
RUN npm install -g pnpm@8.10.2
|
||||
|
||||
# Install application dependencies
|
||||
RUN apk update
|
||||
RUN apk add python3 build-base chromium
|
||||
# Install build dependencies
|
||||
RUN apk add --no-cache python3 build-base chromium
|
||||
|
||||
# Set PYHTON env
|
||||
# Set Python environment
|
||||
ENV PYTHON=/usr/bin/python3
|
||||
|
||||
# Install pnpm packages dependencies
|
||||
RUN npm install -g pnpm
|
||||
# Copy package files for dependency installation
|
||||
COPY --chown=node:node package.json pnpm-lock.yaml pnpm-workspace.yaml lerna.json ./
|
||||
COPY --chown=node:node packages/webapp/package.json ./packages/webapp/
|
||||
COPY --chown=node:node shared/bigcapital-utils/package.json ./shared/bigcapital-utils/
|
||||
COPY --chown=node:node shared/pdf-templates/package.json ./shared/pdf-templates/
|
||||
COPY --chown=node:node shared/email-components/package.json ./shared/email-components/
|
||||
|
||||
# Install all dependencies (including devDependencies for build)
|
||||
RUN pnpm install
|
||||
|
||||
# Copy source code for webapp and dependencies
|
||||
COPY --chown=node:node ./packages/webapp ./packages/webapp
|
||||
COPY --chown=node:node ./shared/bigcapital-utils ./shared/bigcapital-utils
|
||||
COPY --chown=node:node ./shared/pdf-templates ./shared/pdf-templates
|
||||
COPY --chown=node:node ./shared/email-components ./shared/email-components
|
||||
|
||||
# Build webapp package
|
||||
RUN pnpm run build:webapp
|
||||
|
||||
FROM nginx
|
||||
# Stage 2: Nginx
|
||||
FROM nginx:alpine
|
||||
|
||||
COPY ./packages/webapp/nginx/sites/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=build /app/packages/webapp/dist /usr/share/nginx/html
|
||||
# Copy nginx configuration
|
||||
COPY --chown=root:root ./packages/webapp/nginx/sites/default.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Copy built webapp assets from builder stage
|
||||
COPY --from=builder --chown=nginx:nginx /app/packages/webapp/dist /usr/share/nginx/html
|
||||
|
||||
# Expose port
|
||||
EXPOSE 80
|
||||
|
||||
# Nginx runs as nginx user by default, which is good for security
|
||||
# No CMD needed as nginx base image already has it
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"@blueprintjs/colors": "4.1.19",
|
||||
"@blueprintjs/core": "^4.20.2",
|
||||
"@blueprintjs/datetime": "^4.4.37",
|
||||
"@blueprintjs/datetime2": "^3.0.10",
|
||||
"@blueprintjs/datetime2": "^0.9.0",
|
||||
"@blueprintjs/popover2": "^1.14.11",
|
||||
"@blueprintjs/select": "^4.9.24",
|
||||
"@blueprintjs/table": "^4.10.12",
|
||||
|
||||
@@ -4,7 +4,12 @@ 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 #ddd;
|
||||
border-bottom: 1px solid var(--x-table-no-results-border-color);
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -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} />
|
||||
) : (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
export default [
|
||||
export const AllocateLandedCostType = [
|
||||
{ name: intl.get('bills'), value: 'Bill' },
|
||||
{ name: intl.get('expenses'), value: 'Expense' },
|
||||
];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @ts-nocheck
|
||||
export default {
|
||||
export const App = {
|
||||
"app_name": "BigCapital",
|
||||
"app_version": "0.0.1 (build 12344)",
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
export default [
|
||||
export const ContactsOptions = [
|
||||
{ name: intl.get('customer'), path: 'customers' },
|
||||
{ name: intl.get('vendor'), path: 'vendors' },
|
||||
];
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
VendorAction,
|
||||
} from './abilityOption';
|
||||
|
||||
export default [
|
||||
export const KeyboardShortcutsOptions = [
|
||||
{
|
||||
shortcut_key: 'Shift + I',
|
||||
description: intl.get('jump_to_the_invoices'),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from '@/components';
|
||||
|
||||
export default [
|
||||
export const PreferencesMenu = [
|
||||
{
|
||||
text: <T id={'general'} />,
|
||||
disabled: false,
|
||||
@@ -13,10 +13,10 @@ export default [
|
||||
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 default [
|
||||
disabled: false,
|
||||
href: '/preferences/items',
|
||||
},
|
||||
{
|
||||
text: 'Integrations',
|
||||
disabled: false,
|
||||
href: '/preferences/integrations'
|
||||
},
|
||||
// {
|
||||
// text: 'Integrations',
|
||||
// disabled: false,
|
||||
// href: '/preferences/integrations'
|
||||
// },
|
||||
{
|
||||
text: 'API Keys',
|
||||
disabled: false,
|
||||
|
||||
@@ -8,6 +8,11 @@ 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.
|
||||
*/
|
||||
@@ -26,13 +31,13 @@ export const accountNameAccessor = (account) => {
|
||||
* Handle delete errors in bulk and singular.
|
||||
*/
|
||||
export const handleDeleteErrors = (errors) => {
|
||||
if (errors.find((e) => e.type === 'ACCOUNT.PREDEFINED')) {
|
||||
if (errors.find((e) => e.type === DeleteAccountTypeError.AccountPredefined)) {
|
||||
AppToaster.show({
|
||||
message: intl.get('cannot_delete_predefined_accounts'),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
if (errors.find((e) => e.type === 'ACCOUNT.HAS.ASSOCIATED.TRANSACTIONS')) {
|
||||
if (errors.find((e) => e.type === DeleteAccountTypeError.AccountHasAssociatedTransactions)) {
|
||||
AppToaster.show({
|
||||
message: intl.get('cannot_delete_account_has_associated_transactions'),
|
||||
intent: Intent.DANGER,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { queryCache } from 'react-query';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import { FormattedMessage as T, AppToaster } from '@/components';
|
||||
|
||||
import { withAlertStoreConnect } from '@/containers/Alert/withAlertStoreConnect';
|
||||
@@ -22,6 +22,7 @@ function AccountBulkActivateAlert({
|
||||
requestBulkActivateAccounts,
|
||||
}) {
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
const queryClient = useQueryClient();
|
||||
const selectedRowsCount = 0;
|
||||
|
||||
// Handle alert cancel.
|
||||
@@ -38,9 +39,9 @@ function AccountBulkActivateAlert({
|
||||
message: intl.get('the_accounts_has_been_successfully_activated'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
queryCache.invalidateQueries('accounts-table');
|
||||
queryClient.invalidateQueries('accounts-table');
|
||||
})
|
||||
.catch((errors) => { })
|
||||
.catch((errors) => {})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
closeAlert(name);
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { useState } from 'react';
|
||||
import { FormattedMessage as T } from '@/components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { queryCache } from 'react-query';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import { AppToaster } from '@/components';
|
||||
|
||||
// import { withAccountsActions } from '@/containers/Accounts/withAccountsTableActions';
|
||||
@@ -22,8 +22,8 @@ function AccountBulkInactivateAlert({
|
||||
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
const queryClient = useQueryClient();
|
||||
const selectedRowsCount = 0;
|
||||
|
||||
// Handle alert cancel.
|
||||
@@ -39,9 +39,9 @@ function AccountBulkInactivateAlert({
|
||||
message: intl.get('the_accounts_have_been_successfully_inactivated'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
queryCache.invalidateQueries('accounts-table');
|
||||
queryClient.invalidateQueries('accounts-table');
|
||||
})
|
||||
.catch((errors) => { })
|
||||
.catch((errors) => {})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
closeAlert(name);
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { useCallback } from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { AppToaster, FormattedMessage as T } from '@/components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { queryCache } from 'react-query';
|
||||
import { useQueryClient } from 'react-query';
|
||||
|
||||
import { useApproveEstimate } from '@/hooks/query';
|
||||
|
||||
@@ -25,6 +25,7 @@ function EstimateApproveAlert({
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
const queryClient = useQueryClient();
|
||||
const { mutateAsync: deliverEstimateMutate, isLoading } =
|
||||
useApproveEstimate();
|
||||
|
||||
@@ -40,7 +41,7 @@ function EstimateApproveAlert({
|
||||
message: intl.get('the_estimate_has_been_approved_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
queryCache.invalidateQueries('estimates-table');
|
||||
queryClient.invalidateQueries('estimates-table');
|
||||
})
|
||||
.catch((error) => {})
|
||||
.finally(() => {
|
||||
|
||||
@@ -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'}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { DialogContent } from '@/components';
|
||||
import { useQuery, queryCache } from 'react-query';
|
||||
import { useQuery, useQueryClient } from 'react-query';
|
||||
|
||||
import ReferenceNumberForm from '@/containers/JournalNumber/ReferenceNumberForm';
|
||||
|
||||
@@ -31,6 +31,7 @@ function BillNumberDialogContent({
|
||||
// #withBillsActions
|
||||
setBillNumberChanged,
|
||||
}) {
|
||||
const queryClient = useQueryClient();
|
||||
const fetchSettings = useQuery(['settings'], () => requestFetchOptions({}));
|
||||
|
||||
const handleSubmitForm = (values, { setSubmitting }) => {
|
||||
@@ -45,7 +46,7 @@ function BillNumberDialogContent({
|
||||
setBillNumberChanged(true);
|
||||
|
||||
setTimeout(() => {
|
||||
queryCache.invalidateQueries('settings');
|
||||
queryClient.invalidateQueries('settings');
|
||||
}, 250);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { FormattedMessage as T } from '@/components';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useContactDuplicateFromContext } from './ContactDuplicateProvider';
|
||||
|
||||
import Contacts from '@/constants/contactsOptions';
|
||||
import { ContactsOptions } from '@/constants/contactsOptions';
|
||||
|
||||
import { withDialogActions } from '@/containers/Dialog/withDialogActions';
|
||||
import { compose } from '@/utils';
|
||||
@@ -66,7 +66,7 @@ function ContactDuplicateForm({
|
||||
>
|
||||
<FSelect
|
||||
name={'contact_type'}
|
||||
items={Contacts}
|
||||
items={ContactsOptions}
|
||||
placeholder={<T id={'select_contact'} />}
|
||||
textAccessor={'name'}
|
||||
valueAccessor={'path'}
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
FeatureCan,
|
||||
InputPrependText,
|
||||
} from '@/components';
|
||||
import { FMoneyInputGroup, FFormGroup } from '@/components/Forms';
|
||||
import { FMoneyInputGroup, FFormGroup, FDateInput } from '@/components/Forms';
|
||||
|
||||
import { useCustomerOpeningBalanceContext } from './CustomerOpeningBalanceFormProvider';
|
||||
import { useSetPrimaryBranchToForm } from './utils';
|
||||
@@ -59,28 +59,24 @@ function CustomerOpeningBalanceFields({
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Opening balance at -----------*/}
|
||||
<FastField name={'opening_balance_at'}>
|
||||
{({ form, field: { value } }) => (
|
||||
<FormGroup
|
||||
label={
|
||||
<T id={'customer_opening_balance.label.opening_balance_at'} />
|
||||
}
|
||||
className={Classes.FILL}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
onChange={handleDateChange((formattedDate) => {
|
||||
form.setFieldValue('opening_balance_at', formattedDate);
|
||||
})}
|
||||
value={tansformDateValue(value)}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
inputProps={{
|
||||
leftIcon: <Icon icon={'date-range'} />,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<FFormGroup
|
||||
name={'opening_balance_at'}
|
||||
label={<T id={'customer_opening_balance.label.opening_balance_at'} />}
|
||||
fill
|
||||
fastField
|
||||
>
|
||||
<FDateInput
|
||||
name={'opening_balance_at'}
|
||||
formatDate={(date) => date.toLocaleDateString()}
|
||||
parseDate={(str) => new Date(str)}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
inputProps={{
|
||||
leftIcon: <Icon icon={'date-range'} />,
|
||||
}}
|
||||
fill
|
||||
fastField
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
<If condition={!isEqual(base_currency, customer.currency_code)}>
|
||||
{/*------------ Opening balance exchange rate -----------*/}
|
||||
@@ -99,12 +95,15 @@ function CustomerOpeningBalanceFields({
|
||||
<FFormGroup
|
||||
label={<T id={'branch'} />}
|
||||
name={'opening_balance_branch_id'}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
fill
|
||||
fastField
|
||||
>
|
||||
<BranchSelect
|
||||
name={'opening_balance_branch_id'}
|
||||
branches={branches}
|
||||
popoverProps={{ minimal: true }}
|
||||
fastField
|
||||
fill
|
||||
/>
|
||||
</FFormGroup>
|
||||
</FeatureCan>
|
||||
|
||||
@@ -44,6 +44,7 @@ function CustomerOpeningBalanceForm({
|
||||
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
|
||||
const formValues = {
|
||||
...values,
|
||||
opening_balance_at: moment(values.opening_balance_at).format('YYYY-MM-DD'),
|
||||
};
|
||||
|
||||
// Handle request response success.
|
||||
|
||||
@@ -45,6 +45,7 @@ function VendorOpeningBalanceForm({
|
||||
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
|
||||
const formValues = {
|
||||
...values,
|
||||
opening_balance_at: moment(values.opening_balance_at).format('YYYY-MM-DD'),
|
||||
};
|
||||
|
||||
// Handle request response success.
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
FeatureCan,
|
||||
InputPrependText,
|
||||
} from '@/components';
|
||||
import { FMoneyInputGroup, FFormGroup } from '@/components/Forms';
|
||||
import { FMoneyInputGroup, FFormGroup, FDateInput } from '@/components/Forms';
|
||||
|
||||
import { useVendorOpeningBalanceContext } from './VendorOpeningBalanceFormProvider';
|
||||
import { useSetPrimaryBranchToForm } from './utils';
|
||||
@@ -59,26 +59,24 @@ function VendorOpeningBalanceFormFields({
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Opening balance at -----------*/}
|
||||
<FastField name={'opening_balance_at'}>
|
||||
{({ form, field: { value } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'vendor_opening_balance.label.opening_balance_at'} />}
|
||||
className={Classes.FILL}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
onChange={handleDateChange((formattedDate) => {
|
||||
form.setFieldValue('opening_balance_at', formattedDate);
|
||||
})}
|
||||
value={tansformDateValue(value)}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
inputProps={{
|
||||
leftIcon: <Icon icon={'date-range'} />,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<FFormGroup
|
||||
name={'opening_balance_at'}
|
||||
label={<T id={'vendor_opening_balance.label.opening_balance_at'} />}
|
||||
fill
|
||||
fastField
|
||||
>
|
||||
<FDateInput
|
||||
name={'opening_balance_at'}
|
||||
formatDate={(date) => date.toLocaleDateString()}
|
||||
parseDate={(str) => new Date(str)}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
inputProps={{
|
||||
leftIcon: <Icon icon={'date-range'} />,
|
||||
}}
|
||||
fill
|
||||
fastField
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
<If condition={!isEqual(base_currency, vendor.currency_code)}>
|
||||
{/*------------ Opening balance exchange rate -----------*/}
|
||||
@@ -97,7 +95,8 @@ function VendorOpeningBalanceFormFields({
|
||||
<FFormGroup
|
||||
label={<T id={'branch'} />}
|
||||
name={'opening_balance_branch_id'}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
fill
|
||||
fastField
|
||||
>
|
||||
<BranchSelect
|
||||
name={'opening_balance_branch_id'}
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { getColumnWidth } from '@/utils';
|
||||
import * as R from 'ramda';
|
||||
import { useGeneralLedgerContext } from './GeneralLedgerProvider';
|
||||
import { Align } from '@/constants';
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
const getTableCellValueAccessor = (index) => `cells[${index}].value`;
|
||||
|
||||
@@ -75,6 +87,16 @@ 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);
|
||||
|
||||
@@ -88,6 +110,7 @@ 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),
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
// @ts-nocheck
|
||||
import { Align } from '@/constants';
|
||||
import React from 'react';
|
||||
import { Align, CLASSES } 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) => {
|
||||
@@ -86,6 +98,16 @@ const accountCodeColumnAccessor = (column) => {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Description column accessor (muted text in wrapped cell).
|
||||
*/
|
||||
const descriptionColumnAccessor = (column) => {
|
||||
return {
|
||||
...column,
|
||||
Cell: DescriptionCell,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Dynamic column mapper.
|
||||
* @param {} data -
|
||||
@@ -105,6 +127,7 @@ 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),
|
||||
@@ -113,7 +136,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) => {
|
||||
|
||||
@@ -14,6 +14,18 @@ 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: '',
|
||||
@@ -74,7 +86,7 @@ export const transitionItemTypeKeyToLabel = (itemTypeKey) => {
|
||||
// handle delete errors.
|
||||
export const handleDeleteErrors = (errors) => {
|
||||
if (
|
||||
errors.find((error) => error.type === 'ITEM_HAS_ASSOCIATED_TRANSACTINS')
|
||||
errors.find((error) => error.type === ItemErrorType.ItemHasAssociatedTransactions)
|
||||
) {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_item_has_associated_transactions'),
|
||||
@@ -84,7 +96,7 @@ export const handleDeleteErrors = (errors) => {
|
||||
|
||||
if (
|
||||
errors.find(
|
||||
(error) => error.type === 'ITEM_HAS_ASSOCIATED_INVENTORY_ADJUSTMENT',
|
||||
(error) => error.type === ItemErrorType.ItemHasAssociatedInventoryAdjustment,
|
||||
)
|
||||
) {
|
||||
AppToaster.show({
|
||||
@@ -96,7 +108,7 @@ export const handleDeleteErrors = (errors) => {
|
||||
}
|
||||
if (
|
||||
errors.find(
|
||||
(error) => error.type === 'TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS',
|
||||
(error) => error.type === ItemErrorType.TypeCannotChangeWithItemHasTransactions,
|
||||
)
|
||||
) {
|
||||
AppToaster.show({
|
||||
@@ -107,7 +119,7 @@ export const handleDeleteErrors = (errors) => {
|
||||
});
|
||||
}
|
||||
if (
|
||||
errors.find((error) => error.type === 'ITEM_HAS_ASSOCIATED_TRANSACTIONS')
|
||||
errors.find((error) => error.type === ItemErrorType.ItemHasAssociatedTransactionsPlural)
|
||||
) {
|
||||
AppToaster.show({
|
||||
message: intl.get('item.error.you_could_not_delete_item_has_associated'),
|
||||
@@ -214,10 +226,10 @@ export const transformSubmitRequestErrors = (error) => {
|
||||
} = error;
|
||||
const fields = {};
|
||||
|
||||
if (errors.find((e) => e.type === 'ITEM.NAME.ALREADY.EXISTS')) {
|
||||
if (errors.find((e) => e.type === ItemErrorType.ItemNameExists)) {
|
||||
fields.name = intl.get('the_name_used_before');
|
||||
}
|
||||
if (errors.find((e) => e.type === 'INVENTORY_ACCOUNT_CANNOT_MODIFIED')) {
|
||||
if (errors.find((e) => e.type === ItemErrorType.InventoryAccountCannotModified)) {
|
||||
AppToaster.show({
|
||||
message: intl.get('cannot_change_item_inventory_account'),
|
||||
intent: Intent.DANGER,
|
||||
@@ -225,7 +237,7 @@ export const transformSubmitRequestErrors = (error) => {
|
||||
}
|
||||
if (
|
||||
errors.find(
|
||||
(e) => e.type === 'TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS',
|
||||
(e) => e.type === ItemErrorType.TypeCannotChangeWithItemHasTransactions,
|
||||
)
|
||||
) {
|
||||
AppToaster.show({
|
||||
|
||||
@@ -87,7 +87,7 @@ export function WarehousesGridItemBox({
|
||||
<WarehouseBoxRoot>
|
||||
<WarehouseHeader>
|
||||
<WarehouseTitle>
|
||||
{title} {primary && <Icon icon={'star-18dp'} iconSize={16} />}
|
||||
{title} {primary ? <Icon icon={'star-18dp'} iconSize={16} /> : null}
|
||||
</WarehouseTitle>
|
||||
<WarehouseCode>{code}</WarehouseCode>
|
||||
<WarehouseIcon>
|
||||
@@ -118,12 +118,21 @@ 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 #c8cad0;
|
||||
background: #fff;
|
||||
border: 1px solid var(--x-box-border-color);
|
||||
background: var(--x-box-background-color);
|
||||
margin: 5px 5px 8px;
|
||||
width: 200px;
|
||||
height: 160px;
|
||||
@@ -132,7 +141,7 @@ export const WarehouseBoxRoot = styled.div`
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
border-color: #0153cc;
|
||||
border-color: var(--x-box-hover-border-color);
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -143,9 +152,16 @@ 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: #000;
|
||||
color: var(--x-title-color);
|
||||
white-space: nowrap;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
@@ -154,14 +170,19 @@ export const WarehouseTitle = styled.div`
|
||||
margin: 0;
|
||||
margin-left: 2px;
|
||||
vertical-align: top;
|
||||
color: #e1b31d;
|
||||
color: var(--x-title-icon-color);
|
||||
}
|
||||
`;
|
||||
|
||||
const WarehouseCode = styled.div`
|
||||
--x-code-color: #6b7176;
|
||||
|
||||
.bp4-dark & {
|
||||
--x-code-color: var(--color-muted-text);
|
||||
}
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
color: #6b7176;
|
||||
color: var(--x-code-color);
|
||||
margin-top: 4px;
|
||||
`;
|
||||
|
||||
@@ -178,8 +199,13 @@ 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: #000;
|
||||
color: var(--x-item-color);
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import keyboardShortcuts from '@/constants/keyboardShortcutsOptions';
|
||||
import { KeyboardShortcutsOptions } from '@/constants/keyboardShortcutsOptions';
|
||||
import { useAbilitiesFilter } from '../utils/useAbilityContext';
|
||||
|
||||
/**
|
||||
@@ -10,7 +10,7 @@ export const useKeywordShortcuts = () => {
|
||||
const abilitiesFilter = useAbilitiesFilter();
|
||||
|
||||
return React.useMemo(
|
||||
() => abilitiesFilter(keyboardShortcuts),
|
||||
() => abilitiesFilter(KeyboardShortcutsOptions),
|
||||
[abilitiesFilter],
|
||||
);
|
||||
};
|
||||
|
||||
@@ -180,7 +180,7 @@ export function useEditCustomerOpeningBalance(props) {
|
||||
|
||||
return useMutation(
|
||||
([id, values]) =>
|
||||
apiRequest.post(`customers/${id}/opening_balance`, values),
|
||||
apiRequest.put(`customers/${id}/opening-balance`, values),
|
||||
{
|
||||
onSuccess: (res, [id, values]) => {
|
||||
// Invalidate specific customer.
|
||||
|
||||
@@ -167,7 +167,7 @@ export function useEditVendorOpeningBalance(props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([id, values]) => apiRequest.post(`vendors/${id}/opening_balance`, values),
|
||||
([id, values]) => apiRequest.put(`vendors/${id}/opening-balance`, values),
|
||||
{
|
||||
onSuccess: (res, [id, values]) => {
|
||||
// Invalidate specific vendor.
|
||||
|
||||
@@ -3,6 +3,7 @@ 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';
|
||||
|
||||
/**
|
||||
@@ -19,7 +20,11 @@ export function useRequestQuery(query, axios, props) {
|
||||
|
||||
const states = useQuery(
|
||||
query,
|
||||
() => apiRequest.http({ ...axios, url: `/api/${axios.url}` }),
|
||||
() =>
|
||||
apiRequest.http({
|
||||
...axios,
|
||||
url: `/api/${normalizeApiPath(axios.url)}`,
|
||||
}),
|
||||
props,
|
||||
);
|
||||
// Momerize the default data.
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
useSetGlobalErrors,
|
||||
useAuthToken,
|
||||
} from './state';
|
||||
import { getCookie } from '../utils';
|
||||
import { getCookie, normalizeApiPath } 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/${resource}`, params);
|
||||
return http.get(`/api/${normalizeApiPath(resource)}`, params);
|
||||
},
|
||||
|
||||
post(resource, params, config) {
|
||||
return http.post(`/api/${resource}`, params, config);
|
||||
return http.post(`/api/${normalizeApiPath(resource)}`, params, config);
|
||||
},
|
||||
|
||||
update(resource, slug, params) {
|
||||
return http.put(`/api/${resource}/${slug}`, params);
|
||||
return http.put(`/api/${normalizeApiPath(resource)}/${slug}`, params);
|
||||
},
|
||||
|
||||
put(resource, params) {
|
||||
return http.put(`/api/${resource}`, params);
|
||||
return http.put(`/api/${normalizeApiPath(resource)}`, params);
|
||||
},
|
||||
|
||||
patch(resource, params, config) {
|
||||
return http.patch(`/api/${resource}`, params, config);
|
||||
return http.patch(`/api/${normalizeApiPath(resource)}`, params, config);
|
||||
},
|
||||
|
||||
delete(resource, params) {
|
||||
return http.delete(`/api/${resource}`, params);
|
||||
return http.delete(`/api/${normalizeApiPath(resource)}`, params);
|
||||
},
|
||||
}),
|
||||
[http],
|
||||
@@ -130,22 +130,22 @@ export function useAuthApiRequest() {
|
||||
() => ({
|
||||
http,
|
||||
get(resource, params) {
|
||||
return http.get(`/api/${resource}`, params);
|
||||
return http.get(`/api/${normalizeApiPath(resource)}`, params);
|
||||
},
|
||||
post(resource, params, config) {
|
||||
return http.post(`/api/${resource}`, params, config);
|
||||
return http.post(`/api/${normalizeApiPath(resource)}`, params, config);
|
||||
},
|
||||
update(resource, slug, params) {
|
||||
return http.put(`/api/${resource}/${slug}`, params);
|
||||
return http.put(`/api/${normalizeApiPath(resource)}/${slug}`, params);
|
||||
},
|
||||
put(resource, params) {
|
||||
return http.put(`/api/${resource}`, params);
|
||||
return http.put(`/api/${normalizeApiPath(resource)}`, params);
|
||||
},
|
||||
patch(resource, params, config) {
|
||||
return http.patch(`/api/${resource}`, params, config);
|
||||
return http.patch(`/api/${normalizeApiPath(resource)}`, params, config);
|
||||
},
|
||||
delete(resource, params) {
|
||||
return http.delete(`/api/${resource}`, params);
|
||||
return http.delete(`/api/${normalizeApiPath(resource)}`, params);
|
||||
},
|
||||
}),
|
||||
[http],
|
||||
|
||||
@@ -11,7 +11,10 @@ export const getPreferenceRoutes = () => [
|
||||
},
|
||||
{
|
||||
path: `${BASE_URL}/branding`,
|
||||
component: lazy(() => import('../containers/Preferences/Branding/PreferencesBrandingPage')),
|
||||
component: lazy(
|
||||
() =>
|
||||
import('../containers/Preferences/Branding/PreferencesBrandingPage'),
|
||||
),
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
@@ -29,14 +32,20 @@ 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,
|
||||
},
|
||||
@@ -112,16 +121,6 @@ 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')),
|
||||
|
||||
@@ -196,7 +196,6 @@ $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;
|
||||
@@ -209,7 +208,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: rgb(31, 50, 85);
|
||||
--color-financial-sheet-footer-text: var(--color-muted-text);
|
||||
--color-financial-sheet-minimal-title-text: #333;
|
||||
|
||||
// Transaction locking.
|
||||
@@ -514,7 +513,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-light-gray1);
|
||||
--color-financial-sheet-footer-text: var(--color-muted-text);
|
||||
--color-financial-sheet-minimal-title-text: var(--color-white);
|
||||
|
||||
// Transaction locking.
|
||||
|
||||
@@ -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,3 +375,32 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,9 @@ 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);
|
||||
|
||||
|
||||
213
pnpm-lock.yaml
generated
213
pnpm-lock.yaml
generated
@@ -54,6 +54,15 @@ importers:
|
||||
'@bigcapital/utils':
|
||||
specifier: '*'
|
||||
version: link:../../shared/bigcapital-utils
|
||||
'@bull-board/api':
|
||||
specifier: ^5.22.0
|
||||
version: 5.23.0(@bull-board/ui@5.23.0)
|
||||
'@bull-board/express':
|
||||
specifier: ^5.22.0
|
||||
version: 5.23.0
|
||||
'@bull-board/nestjs':
|
||||
specifier: ^5.22.0
|
||||
version: 5.23.0(@bull-board/api@5.23.0)(@bull-board/express@5.23.0)(@nestjs/common@10.4.7)(@nestjs/core@10.4.7)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
'@casl/ability':
|
||||
specifier: ^5.4.3
|
||||
version: 5.4.4
|
||||
@@ -411,7 +420,7 @@ importers:
|
||||
version: 0.3.7(@babel/core@7.28.5)(@blueprintjs/core@4.20.2)(@blueprintjs/select@4.9.24)(formik@2.4.6)(react-dom@18.3.1)(react-is@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs-formik/datetime':
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0(@babel/core@7.28.5)(@blueprintjs-formik/core@0.3.7)(@blueprintjs/core@4.20.2)(@blueprintjs/datetime2@3.0.10)(@blueprintjs/datetime@4.4.37)(@blueprintjs/select@4.9.24)(formik@2.4.6)(react-dom@18.3.1)(react-is@18.3.1)(react@18.3.1)
|
||||
version: 0.4.0(@babel/core@7.28.5)(@blueprintjs-formik/core@0.3.7)(@blueprintjs/core@4.20.2)(@blueprintjs/datetime2@0.9.37)(@blueprintjs/datetime@4.4.37)(@blueprintjs/select@4.9.24)(formik@2.4.6)(react-dom@18.3.1)(react-is@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs-formik/select':
|
||||
specifier: ^0.4.5
|
||||
version: 0.4.5(@blueprintjs/core@4.20.2)(@blueprintjs/select@4.9.24)(formik@2.4.6)(react-dom@18.3.1)(react@18.3.1)
|
||||
@@ -425,8 +434,8 @@ importers:
|
||||
specifier: ^4.4.37
|
||||
version: 4.4.37(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/datetime2':
|
||||
specifier: ^3.0.10
|
||||
version: 3.0.10(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
specifier: ^0.9.0
|
||||
version: 0.9.37(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/popover2':
|
||||
specifier: ^1.14.11
|
||||
version: 1.14.11(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
@@ -4636,7 +4645,7 @@ packages:
|
||||
- react-is
|
||||
dev: false
|
||||
|
||||
/@blueprintjs-formik/datetime@0.4.0(@babel/core@7.28.5)(@blueprintjs-formik/core@0.3.7)(@blueprintjs/core@4.20.2)(@blueprintjs/datetime2@3.0.10)(@blueprintjs/datetime@4.4.37)(@blueprintjs/select@4.9.24)(formik@2.4.6)(react-dom@18.3.1)(react-is@18.3.1)(react@18.3.1):
|
||||
/@blueprintjs-formik/datetime@0.4.0(@babel/core@7.28.5)(@blueprintjs-formik/core@0.3.7)(@blueprintjs/core@4.20.2)(@blueprintjs/datetime2@0.9.37)(@blueprintjs/datetime@4.4.37)(@blueprintjs/select@4.9.24)(formik@2.4.6)(react-dom@18.3.1)(react-is@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-51RW41DSM96seIgOGz2gw2DX1SfH8+Ydi9JBCNmLaFZRqyKZgRX+qpxmltgbS97lk2IOfCEyKSxoGGfz4XlnPg==}
|
||||
peerDependencies:
|
||||
'@blueprintjs-formik/core': ^0.3.2 || 4 || 5
|
||||
@@ -4651,7 +4660,7 @@ packages:
|
||||
'@blueprintjs-formik/core': 0.3.7(@babel/core@7.28.5)(@blueprintjs/core@4.20.2)(@blueprintjs/select@4.9.24)(formik@2.4.6)(react-dom@18.3.1)(react-is@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/core': 4.20.2(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/datetime': 4.4.37(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/datetime2': 3.0.10(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/datetime2': 0.9.37(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/select': 4.9.24(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
formik: 2.4.6(react@18.3.1)
|
||||
lodash.get: 4.4.2
|
||||
@@ -4694,12 +4703,6 @@ packages:
|
||||
tslib: 2.5.3
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/colors@5.1.12:
|
||||
resolution: {integrity: sha512-7GQWUQ82eLE1te++DC8fRO2B31bsSwia82NLamZfKgjHY9V4zxafMT1DK5gKlmmy0nCjpdcCc+df4aVZMHGLww==}
|
||||
dependencies:
|
||||
tslib: 2.6.2
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/core@4.20.2(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-5v4Nr0jozfAjiOkjY4zvt1XSpt4ldnrSaxtwo506S2cxJYfwFeMTmDshXNPFcc8L1fjZMxi0IWI2WABXzZXS6w==}
|
||||
hasBin: true
|
||||
@@ -4727,51 +4730,28 @@ packages:
|
||||
tslib: 2.5.3
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/core@6.4.1(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-JSA8seGiZ2dpmugO7Cea4bh0XEkTioDyzM1Z/0piFsziVv4OeV3jrmMXMegtCdHqpwpWYj8reGuPzFKHGemXXw==}
|
||||
hasBin: true
|
||||
/@blueprintjs/datetime2@0.9.37(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-eCoQbQK1IO5uegmYugj/K5helj6oA7s/4ZkA7dweBHBHXQ3KmZLh2Zj+O0ESOq/qqvlvCfiNCfsObk309EIbKQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '18'
|
||||
react: '18'
|
||||
react-dom: '18'
|
||||
'@types/react': ^16.14.32 || 17
|
||||
react: ^16.8 || 17
|
||||
react-dom: ^16.8 || 17
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@blueprintjs/colors': 5.1.12
|
||||
'@blueprintjs/icons': 6.3.2(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@popperjs/core': 2.11.8
|
||||
'@blueprintjs/core': 4.20.2(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/datetime': 4.4.37(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/popover2': 1.14.11(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/select': 4.9.24(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@types/react': 18.3.4
|
||||
classnames: 2.5.1
|
||||
normalize.css: 8.0.1
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
react-popper: 2.3.0(@popperjs/core@2.11.8)(react-dom@18.3.1)(react@18.3.1)
|
||||
react-transition-group: 4.4.5(react-dom@18.3.1)(react@18.3.1)
|
||||
tslib: 2.6.2
|
||||
use-sync-external-store: 1.6.0(react@18.3.1)
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/datetime2@3.0.10(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-pli0hkv93aH2Ou3inZ2KIdNBLj5dYW8XVu21Rr3fSoh5b9aUsThAVDmb51LjlfNiPVi1i+y2omGuDRTKV+zUgw==}
|
||||
peerDependencies:
|
||||
'@types/react': '18'
|
||||
react: '18'
|
||||
react-dom: '18'
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@blueprintjs/colors': 5.1.12
|
||||
'@blueprintjs/core': 6.4.1(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/datetime': 6.0.10(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/icons': 6.3.2(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@types/react': 18.3.4
|
||||
date-fns: 2.30.0
|
||||
date-fns-tz: 1.3.8(date-fns@2.30.0)
|
||||
lodash: 4.17.21
|
||||
react: 18.3.1
|
||||
react-day-picker: 8.10.1(date-fns@2.30.0)(react@18.3.1)
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
tslib: 2.6.2
|
||||
tslib: 2.5.3
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/datetime@4.4.37(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1):
|
||||
@@ -4793,30 +4773,6 @@ packages:
|
||||
tslib: 2.5.3
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/datetime@6.0.10(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-8i8P12OwCe0JGTkX20EtHvembEjwfqkf1pQcec3lMdPDDeEpPmV3vzPWxwCS+NSc6vUGl/2aAFs+Ro7qDW1msw==}
|
||||
peerDependencies:
|
||||
'@types/react': '18'
|
||||
react: '18'
|
||||
react-dom: '18'
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@blueprintjs/core': 6.4.1(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/icons': 6.3.2(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/select': 6.0.10(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@types/react': 18.3.4
|
||||
classnames: 2.5.1
|
||||
date-fns: 2.30.0
|
||||
date-fns-tz: 2.0.1(date-fns@2.30.0)
|
||||
react: 18.3.1
|
||||
react-day-picker: 8.10.1(date-fns@2.30.0)(react@18.3.1)
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
react-innertext: 1.1.5(@types/react@18.3.4)(react@18.3.1)
|
||||
tslib: 2.6.2
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/icons@4.16.0:
|
||||
resolution: {integrity: sha512-cyfgjUZcZCtQrXWUV8FwqYTFEzduV4a0N7yhOU38jY+cBRCLu/sDrD0Osvfk4DGRvNe4YjY7pohVLFSxpg68Uw==}
|
||||
dependencies:
|
||||
@@ -4825,24 +4781,6 @@ packages:
|
||||
tslib: 2.5.3
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/icons@6.3.2(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-rOdSg7yPmjtqy3e+wD66tXCrzt57M59htVOikwunTaCyQ61+bsF0PyqLfNlhsw1mXqZm8uxSINs1bhnwXhQCiw==}
|
||||
peerDependencies:
|
||||
'@types/react': '18'
|
||||
react: '18'
|
||||
react-dom: '18'
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': 18.3.4
|
||||
change-case: 4.1.2
|
||||
classnames: 2.5.1
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
tslib: 2.6.2
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/popover2@1.14.11(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-5XAjeb2mlWjYXC0pqrNDLzHSsX85Zaiv8jixxUN9abarMUUFKGATgGF8MRsWTLAW94Gli6CB1lzVkrYkRHHf6Q==}
|
||||
peerDependencies:
|
||||
@@ -4884,26 +4822,6 @@ packages:
|
||||
tslib: 2.5.3
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/select@6.0.10(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-RjDuomn4KhluiN7tVofLLX6S+b+un0mLehvmOSETpqk1C8lHXCxReIXxFiWqTB+YiIgkqQ6aRNjQs35cJBbrfg==}
|
||||
peerDependencies:
|
||||
'@types/react': '18'
|
||||
react: '18'
|
||||
react-dom: '18'
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
dependencies:
|
||||
'@blueprintjs/colors': 5.1.12
|
||||
'@blueprintjs/core': 6.4.1(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@blueprintjs/icons': 6.3.2(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1)
|
||||
'@types/react': 18.3.4
|
||||
classnames: 2.5.1
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
tslib: 2.6.2
|
||||
dev: false
|
||||
|
||||
/@blueprintjs/table@4.10.12(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1):
|
||||
resolution: {integrity: sha512-f5NbjuHQ/1vb5hkT+WsCnjIU3Vp2OCWdyDUopVmgj4qHIgzgX38vP2sLS8aUFlbsEQr/xAU9mfkpp4FT+65mXA==}
|
||||
peerDependencies:
|
||||
@@ -4946,6 +4864,51 @@ packages:
|
||||
tslib: 2.5.3
|
||||
dev: false
|
||||
|
||||
/@bull-board/api@5.23.0(@bull-board/ui@5.23.0):
|
||||
resolution: {integrity: sha512-ZZGsWJ+XBG49GAlNgAL9tTEV6Ms7gMkQnZDbzwUhjGChCKWy62RWuPoZSefNXau9QH9+QzlzHRUeFvt4xr5wiw==}
|
||||
peerDependencies:
|
||||
'@bull-board/ui': 5.23.0
|
||||
dependencies:
|
||||
'@bull-board/ui': 5.23.0
|
||||
redis-info: 3.1.0
|
||||
dev: false
|
||||
|
||||
/@bull-board/express@5.23.0:
|
||||
resolution: {integrity: sha512-t/mHzJMlZBtSKD8v81kbZoexOmtQxKVnHZfHJ0um5vrkHNJJuzKuwbR+n9nf1u89AGdyXoWxqDhBDslxv3zrrQ==}
|
||||
dependencies:
|
||||
'@bull-board/api': 5.23.0(@bull-board/ui@5.23.0)
|
||||
'@bull-board/ui': 5.23.0
|
||||
ejs: 3.1.10
|
||||
express: 4.21.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@bull-board/nestjs@5.23.0(@bull-board/api@5.23.0)(@bull-board/express@5.23.0)(@nestjs/common@10.4.7)(@nestjs/core@10.4.7)(reflect-metadata@0.2.2)(rxjs@7.8.1):
|
||||
resolution: {integrity: sha512-eRJCz6PqKzrlZfd7akc+6/1eXwNO7Fp468TmSo8avlPcKhStZH3fUmCF6xxfRQpm9kn/eDOp+H18CTnSafmwmw==}
|
||||
peerDependencies:
|
||||
'@bull-board/api': ^5.23.0
|
||||
'@bull-board/express': ^5.23.0
|
||||
'@nestjs/common': ^9.0.0 || ^10.0.0
|
||||
'@nestjs/core': ^9.0.0 || ^10.0.0
|
||||
reflect-metadata: ^0.1.13 || ^0.2.0
|
||||
rxjs: ^7.8.1
|
||||
dependencies:
|
||||
'@bull-board/api': 5.23.0(@bull-board/ui@5.23.0)
|
||||
'@bull-board/express': 5.23.0
|
||||
'@nestjs/bull-shared': 10.2.2(@nestjs/common@10.4.7)(@nestjs/core@10.4.7)
|
||||
'@nestjs/common': 10.4.7(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
'@nestjs/core': 10.4.7(@nestjs/common@10.4.7)(@nestjs/platform-express@10.4.7)(@nestjs/websockets@10.4.20)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
reflect-metadata: 0.2.2
|
||||
rxjs: 7.8.1
|
||||
dev: false
|
||||
|
||||
/@bull-board/ui@5.23.0:
|
||||
resolution: {integrity: sha512-iI/Ssl8T5ZEn9s899Qz67m92M6RU8thf/aqD7cUHB2yHmkCjqbw7s7NaODTsyArAsnyu7DGJMWm7EhbfFXDNgQ==}
|
||||
dependencies:
|
||||
'@bull-board/api': 5.23.0(@bull-board/ui@5.23.0)
|
||||
dev: false
|
||||
|
||||
/@casl/ability@5.4.4:
|
||||
resolution: {integrity: sha512-7+GOnMUq6q4fqtDDesymBXTS9LSDVezYhFiSJ8Rn3f0aQLeRm7qHn66KWbej4niCOvm0XzNj9jzpkK0yz6hUww==}
|
||||
dependencies:
|
||||
@@ -15031,10 +14994,10 @@ packages:
|
||||
is-data-view: 1.0.1
|
||||
dev: true
|
||||
|
||||
/date-fns-tz@2.0.1(date-fns@2.30.0):
|
||||
resolution: {integrity: sha512-fJCG3Pwx8HUoLhkepdsP7Z5RsucUi+ZBOxyM5d0ZZ6c4SdYustq0VMmOu6Wf7bli+yS/Jwp91TOCqn9jMcVrUA==}
|
||||
/date-fns-tz@1.3.8(date-fns@2.30.0):
|
||||
resolution: {integrity: sha512-qwNXUFtMHTTU6CFSFjoJ80W8Fzzp24LntbjFFBgL/faqds4e5mo9mftoRLgr3Vi1trISsg4awSpYVsOQCRnapQ==}
|
||||
peerDependencies:
|
||||
date-fns: 2.x
|
||||
date-fns: '>=2.0.0'
|
||||
dependencies:
|
||||
date-fns: 2.30.0
|
||||
dev: false
|
||||
@@ -15557,7 +15520,6 @@ packages:
|
||||
hasBin: true
|
||||
dependencies:
|
||||
jake: 10.9.1
|
||||
dev: true
|
||||
|
||||
/electron-to-chromium@1.4.782:
|
||||
resolution: {integrity: sha512-JUfU61e8tr+i5Y1FKXcKs+Xe+rJ+CEqm4cgv1kMihPE2EvYHmYyVr3Im/+1+Z5B29Be2EEGCZCwAc6Tazdl1Yg==}
|
||||
@@ -16832,7 +16794,6 @@ packages:
|
||||
resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
|
||||
dependencies:
|
||||
minimatch: 5.1.6
|
||||
dev: true
|
||||
|
||||
/fill-range@7.1.1:
|
||||
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
||||
@@ -18572,7 +18533,6 @@ packages:
|
||||
chalk: 4.1.2
|
||||
filelist: 1.0.4
|
||||
minimatch: 3.1.2
|
||||
dev: true
|
||||
|
||||
/javascript-natural-sort@0.7.1:
|
||||
resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==}
|
||||
@@ -20133,7 +20093,6 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
brace-expansion: 2.0.1
|
||||
dev: true
|
||||
|
||||
/minimatch@8.0.4:
|
||||
resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==}
|
||||
@@ -22488,16 +22447,6 @@ packages:
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
/react-day-picker@8.10.1(date-fns@2.30.0)(react@18.3.1):
|
||||
resolution: {integrity: sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==}
|
||||
peerDependencies:
|
||||
date-fns: ^2.28.0 || ^3.0.0
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
date-fns: 2.30.0
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
/react-docgen-typescript@2.2.2(typescript@5.6.3):
|
||||
resolution: {integrity: sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==}
|
||||
peerDependencies:
|
||||
@@ -23273,6 +23222,12 @@ packages:
|
||||
engines: {node: '>=4'}
|
||||
dev: false
|
||||
|
||||
/redis-info@3.1.0:
|
||||
resolution: {integrity: sha512-ER4L9Sh/vm63DkIE0bkSjxluQlioBiBgf5w1UuldaW/3vPcecdljVDisZhmnCMvsxHNiARTTDDHGg9cGwTfrKg==}
|
||||
dependencies:
|
||||
lodash: 4.17.21
|
||||
dev: false
|
||||
|
||||
/redis-parser@3.0.0:
|
||||
resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -25786,14 +25741,6 @@ packages:
|
||||
tslib: 2.8.1
|
||||
dev: true
|
||||
|
||||
/use-sync-external-store@1.6.0(react@18.3.1):
|
||||
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
dev: false
|
||||
|
||||
/util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
|
||||
|
||||
19
setup.sh
19
setup.sh
@@ -2,7 +2,6 @@
|
||||
# Initialize the essential variables.
|
||||
BRANCH=main
|
||||
CURRENT=$PWD
|
||||
BIGCAPITAL_INSTALL_DIR=$PWD/bigcapital-$(echo $BRANCH | sed -r 's@(\/|" "|\.)@-@g')
|
||||
BIGCAPITAL_CLONE_TEMP_DIR=$(mktemp -d)
|
||||
CPU_ARCH=$(uname -m)
|
||||
|
||||
@@ -20,8 +19,6 @@ else
|
||||
COMPOSE_CMD="docker compose"
|
||||
fi
|
||||
|
||||
REPO=https://github.com/bigcapitalhq/bigcapital
|
||||
|
||||
# Prints the Bigcapital logo once running the script.
|
||||
function print_logo() {
|
||||
clear
|
||||
@@ -142,8 +139,8 @@ function askForAction() {
|
||||
}
|
||||
|
||||
function install() {
|
||||
echo "Installing Bigcaoital.........."
|
||||
echo "installing is going to take few mintues..."
|
||||
echo "Installing Bigcapital.........."
|
||||
echo "installing is going to take few minutes..."
|
||||
download
|
||||
setup_env
|
||||
}
|
||||
@@ -203,10 +200,11 @@ function startServices() {
|
||||
done
|
||||
printf "\r\033[K"
|
||||
echo " API server started successfully ✅"
|
||||
source "${DOCKER_ENV_PATH}"
|
||||
ACCESS_URL=$(grep -E '^BASE_URL=' "$DOCKER_ENV_PATH" 2>/dev/null | cut -d= -f2-)
|
||||
[ -z "$ACCESS_URL" ] && ACCESS_URL="http://localhost"
|
||||
echo " Bigcapital server started successfully ✅"
|
||||
echo ""
|
||||
echo " You can access the application at $WEB_URL"
|
||||
echo " You can access the application at $ACCESS_URL"
|
||||
echo ""
|
||||
|
||||
}
|
||||
@@ -223,20 +221,19 @@ function restartServices() {
|
||||
}
|
||||
|
||||
function viewLogs(){
|
||||
ARG_SERVICE_NAME=$2
|
||||
echo
|
||||
echo "Select a Service you want to view the logs for:"
|
||||
echo " 1) Webapp"
|
||||
echo " 2) API"
|
||||
echo " 3) Migration"
|
||||
echo " 4) Nginx Proxy"
|
||||
echo " 4) Envoy Proxy"
|
||||
echo " 5) MariaDB"
|
||||
echo " 0) Back to Main Menu"
|
||||
echo
|
||||
read -p "Service: " DOCKER_SERVICE_NAME
|
||||
|
||||
until (( DOCKER_SERVICE_NAME >= 0 && DOCKER_SERVICE_NAME <= 5 )); do
|
||||
echo "Invalid selection. Please enter a number between 1 and 11."
|
||||
echo "Invalid selection. Please enter a number between 0 and 5."
|
||||
read -p "Service: " DOCKER_SERVICE_NAME
|
||||
done
|
||||
|
||||
@@ -248,7 +245,7 @@ function viewLogs(){
|
||||
1) viewSpecificLogs "webapp";;
|
||||
2) viewSpecificLogs "server";;
|
||||
3) viewSpecificLogs "database_migration";;
|
||||
4) viewSpecificLogs "nginx";;
|
||||
4) viewSpecificLogs "proxy";;
|
||||
5) viewSpecificLogs "mysql";;
|
||||
0) askForAction;;
|
||||
*) echo "INVALID SERVICE NAME SUPPLIED";;
|
||||
|
||||
Reference in New Issue
Block a user