Compare commits
33 Commits
tax-compli
...
gitpod
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93841af533 | ||
|
|
121d992b68 | ||
|
|
078a7ea51c | ||
|
|
e070ac72dd | ||
|
|
08ac5f4b01 | ||
|
|
e4a7f09dbc | ||
|
|
2c5537efad | ||
|
|
017908600e | ||
|
|
6307ca8935 | ||
|
|
1ed1c9ea1d | ||
|
|
d40de4d22b | ||
|
|
ee62e3e1c2 | ||
|
|
5df454dd30 | ||
|
|
07628ddc37 | ||
|
|
69afa07e3b | ||
|
|
b1a043f699 | ||
|
|
9eaff0785b | ||
|
|
b3a97ed5d5 | ||
|
|
1aaa65edd2 | ||
|
|
efebf424d1 | ||
|
|
597973d5d1 | ||
|
|
ee6bc822c9 | ||
|
|
07e78ebd6a | ||
|
|
8c04a6205b | ||
|
|
d54ea68d46 | ||
|
|
f42c8031c2 | ||
|
|
5261d332b7 | ||
|
|
a0a5d00be3 | ||
|
|
17e055db5e | ||
|
|
b49a021506 | ||
|
|
339559d830 | ||
|
|
22e4d757e4 | ||
|
|
3b98e8de80 |
@@ -60,6 +60,15 @@
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "kochie",
|
||||
"name": "Robert Koch",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/10809884?v=4",
|
||||
"profile": "https://me.kochie.io",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
13
.github/FUNDING.yml
vendored
Normal file
13
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: Bigcapital # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
15
.gitpod.yml
Normal file
15
.gitpod.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
tasks:
|
||||
- name: Init
|
||||
init: pnpm install && docker-compose pull
|
||||
command: cp .env.example .env && && docker-compose up -d && pnpm run dev
|
||||
|
||||
ports:
|
||||
- port: 4000
|
||||
visibility: public
|
||||
onOpen: open-browser
|
||||
- port: 3000
|
||||
visibility: public
|
||||
onOpen: ignore
|
||||
- port: 3306
|
||||
visibility: public
|
||||
onOpen: ignore
|
||||
14
CHANGELOG.md
14
CHANGELOG.md
@@ -2,6 +2,20 @@
|
||||
|
||||
All notable changes to Bigcapital server-side will be in this file.
|
||||
|
||||
# [0.10.1] - 25-09-2023
|
||||
|
||||
* Fix: Running tenants migration on Docker migration container by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/242
|
||||
|
||||
# [0.10.0] - 24-09-2023
|
||||
|
||||
* Added: Tax rates service by @abouolia @elforjani13 in https://github.com/bigcapitalhq/bigcapital/pull/204
|
||||
* Added: Sales Tax Liability Summary report by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/204
|
||||
* Added: Tax rates tracking with sale invoices by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/204
|
||||
* fix(webapp): Table headers sticky for all reports. by @elforjani13 in https://github.com/bigcapitalhq/bigcapital/pull/240
|
||||
* chore(deps): bump word-wrap from 1.2.3 to 1.2.4 by @dependabot in https://github.com/bigcapitalhq/bigcapital/pull/200
|
||||
* chore(deps): bump word-wrap from 1.2.3 to 1.2.4 in /packages/webapp by @dependabot in https://github.com/bigcapitalhq/bigcapital/pull/199
|
||||
* chore(deps): bump mongoose from 5.13.15 to 5.13.20 by @dependabot in https://github.com/bigcapitalhq/bigcapital/pull/197
|
||||
|
||||
# [0.9.12] - 29-08-2023
|
||||
|
||||
* Refactor: split the services to multiple service classes. (by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/202)
|
||||
|
||||
@@ -33,7 +33,8 @@ Contributions via pull requests are much appreciated. Once the approach is agree
|
||||
---
|
||||
|
||||
## Local Setup Prerequisites
|
||||
- The application currently supports **Node.js v14.x**. Please ensure that you are using this version of Node.js when developing. (use [nvm](https://github.com/nvm-sh/nvm#installing-and-updating) to switch between node versions)
|
||||
- The application currently supports **Node.js v18.x**.
|
||||
- `pnpm` packages manager, (from pnpm [guide](https://pnpm.io/installation) pick any installation method).
|
||||
|
||||
## Contribute to Backend
|
||||
|
||||
@@ -44,11 +45,10 @@ Contributions via pull requests are much appreciated. Once the approach is agree
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
- Install all npm dependencies of the monorepo, you don't have to change directory to the `backend` package. just hit these command on root directory and it will install dependencies of all packages.
|
||||
- Install all npm dependencies of the monorepo, you don't have to change directory to the `backend` package. just hit the command on root directory and it will install dependencies of all packages.
|
||||
|
||||
```
|
||||
npm install
|
||||
npm run bootstrap
|
||||
pnpm install
|
||||
```
|
||||
|
||||
- Run all required docker containers in the development, we already configured all containers under `docker-compose.yml`.
|
||||
@@ -69,7 +69,7 @@ cefa73fe2881 bigcapital-redis "docker-entrypoint.s…" 7 seconds ago Up
|
||||
- There're some CLI commands we should run before running the server like databaase migration, so we need to build the `server` app first.
|
||||
|
||||
```
|
||||
npm run build:server
|
||||
pnpm run build:server
|
||||
```
|
||||
|
||||
- Run the database migration for system database.
|
||||
@@ -87,7 +87,7 @@ Batch 1 run: 6 migrations
|
||||
- Next, start the webapp application.
|
||||
|
||||
```
|
||||
npm run dev:server
|
||||
pnpm run dev:server
|
||||
```
|
||||
|
||||
**[`^top^`](#)**
|
||||
@@ -105,14 +105,13 @@ git clone https://github.com/bigcapital/bigcapital.git && cd bigcaptial
|
||||
- Install all npm dependencies of the monorepo, you don't have to change directory to the `frontend` package. just hit that command and will install all packages across all application.
|
||||
|
||||
```
|
||||
npm install
|
||||
npm run bootstrap
|
||||
pnpm install
|
||||
```
|
||||
|
||||
- Next, start the webapp application.
|
||||
|
||||
```
|
||||
npm run dev:webapp
|
||||
pnpm run dev:webapp
|
||||
```
|
||||
|
||||
**[`^top^`](#)**
|
||||
|
||||
@@ -48,6 +48,12 @@ Bigcapital is a smart and open-source accounting and inventory software, Bigcapi
|
||||
|
||||
Please see [Releases](https://github.com/bigcapitalhq/bigcapital/releases) for more information what has changed recently.
|
||||
|
||||
# Contact us
|
||||
|
||||
Meet our sales team for any commercial inquiries.
|
||||
|
||||
<a target="_blank" href="https://cal.com/ahmed-bouhuolia-ekk3ph/30min"><img src="https://cal.com/book-with-cal-dark.svg" alt="Book us with Cal.com"></a>
|
||||
|
||||
# Recognition
|
||||
|
||||
<a href="https://news.ycombinator.com/item?id=36118990">
|
||||
@@ -74,6 +80,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://scheibling.se"><img src="https://avatars.githubusercontent.com/u/24367830?v=4?s=100" width="100px;" alt="Lars Scheibling"/><br /><sub><b>Lars Scheibling</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Ascheibling" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/suhaibaffan"><img src="https://avatars.githubusercontent.com/u/18115937?v=4?s=100" width="100px;" alt="Suhaib Affan"/><br /><sub><b>Suhaib Affan</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=suhaibaffan" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/KalliopiPliogka"><img src="https://avatars.githubusercontent.com/u/81677549?v=4?s=100" width="100px;" alt="Kalliopi Pliogka"/><br /><sub><b>Kalliopi Pliogka</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3AKalliopiPliogka" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://me.kochie.io"><img src="https://avatars.githubusercontent.com/u/10809884?v=4?s=100" width="100px;" alt="Robert Koch"/><br /><sub><b>Robert Koch</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=kochie" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -15,8 +15,8 @@ services:
|
||||
- ./data/logs/nginx/:/var/log/nginx
|
||||
- ./docker/certbot/certs/:/var/certs
|
||||
ports:
|
||||
- "${PUBLIC_PROXY_PORT:-80}:80"
|
||||
- "${PUBLIC_PROXY_SSL_PORT:-443}:443"
|
||||
- '${PUBLIC_PROXY_PORT:-80}:80'
|
||||
- '${PUBLIC_PROXY_SSL_PORT:-443}:443'
|
||||
tty: true
|
||||
depends_on:
|
||||
- server
|
||||
@@ -71,7 +71,7 @@ services:
|
||||
# Authentication
|
||||
- JWT_SECRET=${JWT_SECRET}
|
||||
|
||||
# MongoDB
|
||||
# MongoDB
|
||||
- MONGODB_DATABASE_URL=mongodb://mongo/bigcapital
|
||||
|
||||
# Application
|
||||
@@ -92,11 +92,14 @@ services:
|
||||
context: ./
|
||||
dockerfile: docker/migration/Dockerfile
|
||||
environment:
|
||||
# Database
|
||||
- DB_HOST=mysql
|
||||
- DB_USER=${DB_USER}
|
||||
- DB_PASSWORD=${DB_PASSWORD}
|
||||
- DB_CHARSET=${DB_CHARSET}
|
||||
- SYSTEM_DB_NAME=${SYSTEM_DB_NAME}
|
||||
# Tenants databases
|
||||
- TENANT_DB_NAME_PERFIX=${TENANT_DB_NAME_PERFIX}
|
||||
depends_on:
|
||||
- mysql
|
||||
|
||||
@@ -136,7 +139,7 @@ services:
|
||||
build:
|
||||
context: ./docker/redis
|
||||
expose:
|
||||
- "6379"
|
||||
- '6379'
|
||||
volumes:
|
||||
- redis:/data
|
||||
|
||||
|
||||
@@ -34,5 +34,7 @@ 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", "--", "node", "./build/commands.js", "system:migrate:latest"]
|
||||
ADD docker/migration/start.sh /
|
||||
RUN chmod +x /start.sh
|
||||
|
||||
CMD ["/start.sh"]
|
||||
5
docker/migration/start.sh
Normal file
5
docker/migration/start.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
# Migrate the master system database.
|
||||
./wait-for-it/wait-for-it.sh mysql:3306 -- node ./build/commands.js system:migrate:latest
|
||||
|
||||
# Migrate all tenants.
|
||||
./wait-for-it/wait-for-it.sh mysql:3306 -- node ./build/commands.js tenants:migrate:latest
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"version": "independent",
|
||||
"npmClient": "pnpm",
|
||||
"useWorkspaces": true,
|
||||
"version": "0.9.6",
|
||||
"npmClient": "npm"
|
||||
}
|
||||
"packages": ["packages/*"]
|
||||
}
|
||||
5730
package-lock.json
generated
5730
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,6 @@
|
||||
"name": "bigcapital-monorepo",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"bootstrap": "lerna exec npm install",
|
||||
"dev": "lerna run dev",
|
||||
"build": "lerna run build",
|
||||
"dev:webapp": "lerna run dev --scope \"@bigcapital/webapp\"",
|
||||
@@ -13,10 +12,6 @@
|
||||
"test:e2e": "playwright test",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"shared/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.4.2",
|
||||
"@commitlint/config-conventional": "^17.4.2",
|
||||
@@ -27,7 +22,7 @@
|
||||
"lerna": "^6.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "14.x"
|
||||
"node": "16.x || 17.x || 18.x"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
|
||||
3
packages/server/.gitignore
vendored
3
packages/server/.gitignore
vendored
@@ -1,7 +1,6 @@
|
||||
/node_modules/
|
||||
/.env
|
||||
/storage
|
||||
package-lock.json
|
||||
stdout.log
|
||||
/dist
|
||||
/build
|
||||
/build
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:14.20-alpine as build
|
||||
FROM node:18.16.0-alpine as build
|
||||
|
||||
USER root
|
||||
|
||||
@@ -83,15 +83,25 @@ WORKDIR /app
|
||||
|
||||
RUN chown node:node /
|
||||
|
||||
# Install pnpm
|
||||
RUN npm install -g pnpm
|
||||
|
||||
# Copy application dependency manifests to the container image.
|
||||
COPY ./package*.json ./
|
||||
COPY ./pnpm-lock.yaml ./pnpm-lock.yaml
|
||||
COPY ./pnpm-workspace.yaml ./pnpm-workspace.yaml
|
||||
COPY ./lerna.json ./lerna.json
|
||||
COPY ./packages/server/package*.json ./packages/server/
|
||||
|
||||
COPY ./lerna.json ./lerna.json
|
||||
# Install application dependencies
|
||||
RUN apk update
|
||||
RUN apk add python3 build-base chromium
|
||||
|
||||
# Install app dependencies for production.
|
||||
RUN npm install
|
||||
RUN npm run bootstrap
|
||||
# Set PYHTON env
|
||||
ENV PYTHON=/usr/bin/python3
|
||||
|
||||
# Install packages dependencies for production.
|
||||
RUN pnpm install
|
||||
|
||||
COPY --chown=node:node ./packages/server ./packages/server
|
||||
|
||||
|
||||
13698
packages/server/package-lock.json
generated
Normal file
13698
packages/server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bigcapital/server",
|
||||
"version": "0.9.5",
|
||||
"version": "0.10.2",
|
||||
"description": "",
|
||||
"main": "src/server.ts",
|
||||
"scripts": {
|
||||
@@ -35,6 +35,7 @@
|
||||
"babel-loader": "^9.1.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bluebird": "^3.7.2",
|
||||
"body-parser": "^1.20.2",
|
||||
"compression": "^1.7.4",
|
||||
"country-codes-list": "^1.6.8",
|
||||
"cpy": "^8.1.2",
|
||||
@@ -42,7 +43,7 @@
|
||||
"crypto-random-string": "^3.2.0",
|
||||
"csurf": "^1.10.0",
|
||||
"deep-map": "^2.0.0",
|
||||
"deepdash": "^5.3.7",
|
||||
"deepdash": "^5.3.9",
|
||||
"dotenv": "^8.1.0",
|
||||
"errorhandler": "^1.5.1",
|
||||
"es6-weak-map": "^2.0.3",
|
||||
@@ -72,6 +73,8 @@
|
||||
"memory-cache": "^0.2.0",
|
||||
"moment": "^2.24.0",
|
||||
"moment-range": "^4.0.2",
|
||||
"moment-timezone": "^0.5.43",
|
||||
"mongodb": "^6.1.0",
|
||||
"mongoose": "^5.10.0",
|
||||
"mustache": "^3.0.3",
|
||||
"mysql": "^2.17.1",
|
||||
@@ -92,6 +95,7 @@
|
||||
"rate-limiter-flexible": "^2.1.14",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rtl-detect": "^1.0.4",
|
||||
"source-map-loader": "^4.0.1",
|
||||
"ts-transformer-keys": "^0.4.2",
|
||||
"tsyringe": "^4.3.0",
|
||||
"typedi": "^0.8.0",
|
||||
@@ -131,7 +135,7 @@
|
||||
"rimraf": "^3.0.2",
|
||||
"rtlcss": "^3.3.0",
|
||||
"run-script-webpack-plugin": "^0.1.1",
|
||||
"sass": "^1.37.5",
|
||||
"sass": "^1.58.0",
|
||||
"sinon": "^7.4.2",
|
||||
"start-server-webpack-plugin": "^2.2.5",
|
||||
"ts-loader": "^9.4.2",
|
||||
|
||||
@@ -587,6 +587,7 @@
|
||||
"balance_sheet.long_term_liabilities": "Long-Term Liabilities",
|
||||
"balance_sheet.non_current_liabilities": "Non-Current Liabilities",
|
||||
"balance_sheet.equity": "Equity",
|
||||
"balance_sheet.net_income": "Net Income",
|
||||
|
||||
"balance_sheet.account_name": "Account name",
|
||||
"balance_sheet.total": "Total",
|
||||
|
||||
@@ -16,7 +16,7 @@ export default class TrialBalanceSheetController extends BaseFinancialReportCont
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
public router() {
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
@@ -36,7 +36,7 @@ export default class TrialBalanceSheetController extends BaseFinancialReportCont
|
||||
* Validation schema.
|
||||
* @return {ValidationChain[]}
|
||||
*/
|
||||
get trialBalanceSheetValidationSchema(): ValidationChain[] {
|
||||
private get trialBalanceSheetValidationSchema(): ValidationChain[] {
|
||||
return [
|
||||
...this.sheetNumberFormatValidationSchema,
|
||||
query('basis').optional(),
|
||||
@@ -59,28 +59,37 @@ export default class TrialBalanceSheetController extends BaseFinancialReportCont
|
||||
/**
|
||||
* Retrieve the trial balance sheet.
|
||||
*/
|
||||
public async trialBalanceSheet(
|
||||
private async trialBalanceSheet(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
const { tenantId, settings } = req;
|
||||
const { tenantId } = req;
|
||||
let filter = this.matchedQueryData(req);
|
||||
|
||||
filter = {
|
||||
...filter,
|
||||
accountsIds: castArray(filter.accountsIds),
|
||||
};
|
||||
|
||||
try {
|
||||
const { data, query, meta } =
|
||||
await this.trialBalanceSheetService.trialBalanceSheet(tenantId, filter);
|
||||
const accept = this.accepts(req);
|
||||
const acceptType = accept.types(['json', 'application/json+table']);
|
||||
|
||||
return res.status(200).send({
|
||||
data: this.transfromToResponse(data),
|
||||
query: this.transfromToResponse(query),
|
||||
meta: this.transfromToResponse(meta),
|
||||
});
|
||||
if (acceptType === 'application/json+table') {
|
||||
const { table, meta, query } =
|
||||
await this.trialBalanceSheetService.trialBalanceSheetTable(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
return res.status(200).send({ table, meta, query });
|
||||
} else {
|
||||
const { data, query, meta } =
|
||||
await this.trialBalanceSheetService.trialBalanceSheet(
|
||||
tenantId,
|
||||
filter
|
||||
);
|
||||
return res.status(200).send({ data, query, meta });
|
||||
}
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
|
||||
@@ -149,6 +149,11 @@ export default class ItemsController extends BaseController {
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: DATATYPES_LENGTH.TEXT }),
|
||||
check('sell_tax_rate_id').optional({ nullable: true }).isInt().toInt(),
|
||||
check('purchase_tax_rate_id')
|
||||
.optional({ nullable: true })
|
||||
.isInt()
|
||||
.toInt(),
|
||||
check('category_id')
|
||||
.optional({ nullable: true })
|
||||
.isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 })
|
||||
@@ -508,6 +513,28 @@ export default class ItemsController extends BaseController {
|
||||
],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'PURCHASE_TAX_RATE_NOT_FOUND') {
|
||||
return res.status(400).send({
|
||||
errors: [
|
||||
{
|
||||
type: 'PURCHASE_TAX_RATE_NOT_FOUND',
|
||||
message: 'Purchase tax rate has not found.',
|
||||
code: 410,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'SELL_TAX_RATE_NOT_FOUND') {
|
||||
return res.status(400).send({
|
||||
errors: [
|
||||
{
|
||||
type: 'SELL_TAX_RATE_NOT_FOUND',
|
||||
message: 'Sell tax rate is not found.',
|
||||
code: 420,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
|
||||
@@ -31,14 +31,14 @@ export default class OrganizationController extends BaseController {
|
||||
|
||||
router.post(
|
||||
'/build',
|
||||
this.organizationValidationSchema,
|
||||
this.buildOrganizationValidationSchema,
|
||||
this.validationResult,
|
||||
asyncMiddleware(this.build.bind(this)),
|
||||
this.handleServiceErrors.bind(this)
|
||||
);
|
||||
router.put(
|
||||
'/',
|
||||
this.organizationValidationSchema,
|
||||
this.updateOrganizationValidationSchema,
|
||||
this.validationResult,
|
||||
this.asyncMiddleware(this.updateOrganization.bind(this)),
|
||||
this.handleServiceErrors.bind(this)
|
||||
@@ -55,7 +55,7 @@ export default class OrganizationController extends BaseController {
|
||||
* Organization setup schema.
|
||||
* @return {ValidationChain[]}
|
||||
*/
|
||||
private get organizationValidationSchema(): ValidationChain[] {
|
||||
private get commonOrganizationValidationSchema(): ValidationChain[] {
|
||||
return [
|
||||
check('name').exists().trim(),
|
||||
check('industry').optional({ nullable: true }).isString().trim().escape(),
|
||||
@@ -68,6 +68,29 @@ export default class OrganizationController extends BaseController {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build organization validation schema.
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
private get buildOrganizationValidationSchema(): ValidationChain[] {
|
||||
return [...this.commonOrganizationValidationSchema];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update organization validation schema.
|
||||
* @returns {ValidationChain[]}
|
||||
*/
|
||||
private get updateOrganizationValidationSchema(): ValidationChain[] {
|
||||
return [
|
||||
...this.commonOrganizationValidationSchema,
|
||||
check('tax_number')
|
||||
.optional({ nullable: true })
|
||||
.isString()
|
||||
.trim()
|
||||
.escape(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds tenant database and migrate database schema.
|
||||
* @param {Request} req - Express request.
|
||||
|
||||
@@ -115,6 +115,8 @@ export default class BillsController extends BaseController {
|
||||
check('note').optional().trim().escape(),
|
||||
check('open').default(false).isBoolean().toBoolean(),
|
||||
|
||||
check('is_inclusive_tax').default(false).isBoolean().toBoolean(),
|
||||
|
||||
check('entries').isArray({ min: 1 }),
|
||||
|
||||
check('entries.*.index').exists().isNumeric().toInt(),
|
||||
@@ -137,6 +139,15 @@ export default class BillsController extends BaseController {
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toInt(),
|
||||
check('entries.*.tax_code')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape()
|
||||
.isString(),
|
||||
check('entries.*.tax_rate_id')
|
||||
.optional({ nullable: true })
|
||||
.isNumeric()
|
||||
.toInt(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -542,6 +553,16 @@ export default class BillsController extends BaseController {
|
||||
],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'ITEM_ENTRY_TAX_RATE_CODE_NOT_FOUND') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'ITEM_ENTRY_TAX_RATE_CODE_NOT_FOUND', code: 1800 }],
|
||||
});
|
||||
}
|
||||
if (error.errorType === 'ITEM_ENTRY_TAX_RATE_ID_NOT_FOUND') {
|
||||
return res.boom.badRequest(null, {
|
||||
errors: [{ type: 'ITEM_ENTRY_TAX_RATE_ID_NOT_FOUND', code: 1900 }],
|
||||
});
|
||||
}
|
||||
}
|
||||
next(error);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
exports.up = (knex) => {
|
||||
return knex.schema.table('bills', (table) => {
|
||||
table.boolean('is_inclusive_tax').defaultTo(false);
|
||||
table.decimal('tax_amount_withheld');
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = (knex) => {
|
||||
return knex.schema.table('bills', () => {});
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
exports.up = (knex) => {
|
||||
return knex.schema.table('items', (table) => {
|
||||
table
|
||||
.integer('sell_tax_rate_id')
|
||||
.unsigned()
|
||||
.references('id')
|
||||
.inTable('tax_rates');
|
||||
table
|
||||
.integer('purchase_tax_rate_id')
|
||||
.unsigned()
|
||||
.references('id')
|
||||
.inTable('tax_rates');
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = (knex) => {
|
||||
return knex.schema.dropTableIfExists('tax_rates');
|
||||
};
|
||||
@@ -9,6 +9,7 @@ export enum BALANCE_SHEET_SCHEMA_NODE_TYPE {
|
||||
AGGREGATE = 'AGGREGATE',
|
||||
ACCOUNTS = 'ACCOUNTS',
|
||||
ACCOUNT = 'ACCOUNT',
|
||||
NET_INCOME = 'NET_INCOME',
|
||||
}
|
||||
|
||||
export enum BALANCE_SHEET_NODE_TYPE {
|
||||
@@ -33,6 +34,7 @@ export enum BALANCE_SHEET_SCHEMA_NODE_ID {
|
||||
LOGN_TERM_LIABILITY = 'LOGN_TERM_LIABILITY',
|
||||
NON_CURRENT_LIABILITY = 'NON_CURRENT_LIABILITY',
|
||||
EQUITY = 'EQUITY',
|
||||
NET_INCOME = 'NET_INCOME',
|
||||
}
|
||||
|
||||
// Balance sheet query.
|
||||
@@ -87,7 +89,6 @@ export interface IBalanceSheetDOO {
|
||||
meta: IBalanceSheetMeta;
|
||||
}
|
||||
|
||||
|
||||
export interface IBalanceSheetCommonNode {
|
||||
total: IBalanceSheetTotal;
|
||||
horizontalTotals?: IBalanceSheetTotal[];
|
||||
@@ -108,7 +109,7 @@ export interface IBalanceSheetAggregateNode extends IBalanceSheetCommonNode {
|
||||
id: string;
|
||||
name: string;
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE;
|
||||
children?: (IBalanceSheetAggregateNode | IBalanceSheetAccountNode)[];
|
||||
children?: IBalanceSheetDataNode[];
|
||||
}
|
||||
|
||||
export interface IBalanceSheetTotal {
|
||||
@@ -118,6 +119,13 @@ export interface IBalanceSheetTotal {
|
||||
date?: string | Date;
|
||||
}
|
||||
|
||||
export interface IBalanceSheetAccountsNode extends IBalanceSheetCommonNode {
|
||||
id: number | string;
|
||||
name: string;
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS;
|
||||
children: IBalanceSheetAccountNode[];
|
||||
}
|
||||
|
||||
export interface IBalanceSheetAccountNode extends IBalanceSheetCommonNode {
|
||||
id: number;
|
||||
index: number;
|
||||
@@ -128,7 +136,17 @@ export interface IBalanceSheetAccountNode extends IBalanceSheetCommonNode {
|
||||
children?: IBalanceSheetAccountNode[];
|
||||
}
|
||||
|
||||
export type IBalanceSheetDataNode = IBalanceSheetAggregateNode;
|
||||
export interface IBalanceSheetNetIncomeNode extends IBalanceSheetCommonNode {
|
||||
id: number;
|
||||
name: string;
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME;
|
||||
}
|
||||
|
||||
export type IBalanceSheetDataNode =
|
||||
| IBalanceSheetAggregateNode
|
||||
| IBalanceSheetAccountNode
|
||||
| IBalanceSheetAccountsNode
|
||||
| IBalanceSheetNetIncomeNode;
|
||||
|
||||
export interface IBalanceSheetPercentageAmount {
|
||||
amount: number;
|
||||
@@ -150,9 +168,16 @@ export interface IBalanceSheetSchemaAccountNode {
|
||||
accountsTypes: string[];
|
||||
}
|
||||
|
||||
export interface IBalanceSheetSchemaNetIncomeNode {
|
||||
id: string;
|
||||
name: string;
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE;
|
||||
}
|
||||
|
||||
export type IBalanceSheetSchemaNode =
|
||||
| IBalanceSheetSchemaAccountNode
|
||||
| IBalanceSheetSchemaAggregateNode;
|
||||
| IBalanceSheetSchemaAggregateNode
|
||||
| IBalanceSheetSchemaNetIncomeNode;
|
||||
|
||||
export interface IBalanceSheetDatePeriods {
|
||||
assocAccountNodeDatePeriods(node): any;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Knex } from 'knex';
|
||||
import { IDynamicListFilterDTO } from './DynamicFilter';
|
||||
import { IItemEntry, IItemEntryDTO } from './ItemEntry';
|
||||
import { IBillLandedCost } from './LandedCost';
|
||||
|
||||
export interface IBillDTO {
|
||||
vendorId: number;
|
||||
billNumber: string;
|
||||
@@ -15,10 +16,10 @@ export interface IBillDTO {
|
||||
exchangeRate?: number;
|
||||
open: boolean;
|
||||
entries: IItemEntryDTO[];
|
||||
|
||||
branchId?: number;
|
||||
warehouseId?: number;
|
||||
projectId?: number;
|
||||
isInclusiveTax?: boolean;
|
||||
}
|
||||
|
||||
export interface IBillEditDTO {
|
||||
@@ -80,6 +81,15 @@ export interface IBill {
|
||||
|
||||
localAmount?: number;
|
||||
locatedLandedCosts?: IBillLandedCost[];
|
||||
|
||||
amountLocal: number;
|
||||
subtotal: number;
|
||||
subtotalLocal: number;
|
||||
subtotalExcludingTax: number;
|
||||
taxAmountWithheld: number;
|
||||
taxAmountWithheldLocal: number;
|
||||
total: number;
|
||||
totalLocal: number;
|
||||
}
|
||||
|
||||
export interface IBillsFilter extends IDynamicListFilterDTO {
|
||||
|
||||
@@ -22,6 +22,9 @@ export interface IItem {
|
||||
sellDescription: string;
|
||||
purchaseDescription: string;
|
||||
|
||||
sellTaxRateId: number;
|
||||
purchaseTaxRateId: number;
|
||||
|
||||
quantityOnHand: number;
|
||||
|
||||
note: string;
|
||||
@@ -54,6 +57,9 @@ export interface IItemDTO {
|
||||
sellDescription: string;
|
||||
purchaseDescription: string;
|
||||
|
||||
sellTaxRateId: number;
|
||||
purchaseTaxRateId: number;
|
||||
|
||||
quantityOnHand: number;
|
||||
|
||||
note: string;
|
||||
|
||||
@@ -7,18 +7,25 @@ export interface ILedger {
|
||||
filter(cb: (entry: ILedgerEntry) => boolean): ILedger;
|
||||
|
||||
whereAccountId(accountId: number): ILedger;
|
||||
whereAccountsIds(accountsIds: number[]): ILedger;
|
||||
whereContactId(contactId: number): ILedger;
|
||||
whereFromDate(fromDate: Date | string): ILedger;
|
||||
whereToDate(toDate: Date | string): ILedger;
|
||||
whereCurrencyCode(currencyCode: string): ILedger;
|
||||
whereBranch(branchId: number): ILedger;
|
||||
whereItem(itemId: number): ILedger;
|
||||
whereProject(projectId: number): ILedger;
|
||||
|
||||
getClosingBalance(): number;
|
||||
getForeignClosingBalance(): number;
|
||||
getClosingDebit(): number;
|
||||
getClosingCredit(): number;
|
||||
|
||||
getContactsIds(): number[];
|
||||
getAccountsIds(): number[];
|
||||
|
||||
reverse(): ILedger;
|
||||
isEmpty(): boolean;
|
||||
}
|
||||
|
||||
export interface ILedgerEntry {
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface IOrganizationUpdateDTO {
|
||||
timezone: string;
|
||||
fiscalYear: string;
|
||||
industry: string;
|
||||
taxNumber: string;
|
||||
}
|
||||
|
||||
export interface IOrganizationBuildEventPayload {
|
||||
|
||||
@@ -36,6 +36,7 @@ export interface ITaxRateCreatedPayload {
|
||||
}
|
||||
|
||||
export interface ITaxRateEditingPayload {
|
||||
oldTaxRate: ITaxRate;
|
||||
editTaxRateDTO: IEditTaxRateDTO;
|
||||
tenantId: number;
|
||||
trx: Knex.Transaction;
|
||||
|
||||
@@ -33,6 +33,7 @@ export interface ITrialBalanceAccount extends ITrialBalanceTotal {
|
||||
id: number;
|
||||
parentAccountId: number;
|
||||
name: string;
|
||||
formattedName: string;
|
||||
code: string;
|
||||
accountNormal: string;
|
||||
}
|
||||
|
||||
@@ -81,6 +81,9 @@ import { ProjectBillableBillSubscriber } from '@/services/Projects/Projects/Proj
|
||||
import { SyncActualTimeTaskSubscriber } from '@/services/Projects/Times/SyncActualTimeTaskSubscriber';
|
||||
import { SaleInvoiceTaxRateValidateSubscriber } from '@/services/TaxRates/subscribers/SaleInvoiceTaxRateValidateSubscriber';
|
||||
import { WriteInvoiceTaxTransactionsSubscriber } from '@/services/TaxRates/subscribers/WriteInvoiceTaxTransactionsSubscriber';
|
||||
import { BillTaxRateValidateSubscriber } from '@/services/TaxRates/subscribers/BillTaxRateValidateSubscriber';
|
||||
import { WriteBillTaxTransactionsSubscriber } from '@/services/TaxRates/subscribers/WriteBillTaxTransactionsSubscriber';
|
||||
import { SyncItemTaxRateOnEditTaxSubscriber } from '@/services/TaxRates/SyncItemTaxRateOnEditTaxSubscriber';
|
||||
|
||||
export default () => {
|
||||
return new EventPublisher();
|
||||
@@ -188,8 +191,14 @@ export const susbcribers = () => {
|
||||
ProjectBillableExpensesSubscriber,
|
||||
ProjectBillableBillSubscriber,
|
||||
|
||||
// Tax Rates
|
||||
// Tax Rates - Sale Invoice
|
||||
SaleInvoiceTaxRateValidateSubscriber,
|
||||
WriteInvoiceTaxTransactionsSubscriber,
|
||||
|
||||
// Tax Rates - Bills
|
||||
BillTaxRateValidateSubscriber,
|
||||
WriteBillTaxTransactionsSubscriber,
|
||||
|
||||
SyncItemTaxRateOnEditTaxSubscriber
|
||||
];
|
||||
};
|
||||
|
||||
@@ -587,6 +587,7 @@
|
||||
"balance_sheet.long_term_liabilities": "Long-Term Liabilities",
|
||||
"balance_sheet.non_current_liabilities": "Non-Current Liabilities",
|
||||
"balance_sheet.equity": "Equity",
|
||||
"balance_sheet.net_income": "Net Income",
|
||||
|
||||
"balance_sheet.account_name": "Account name",
|
||||
"balance_sheet.total": "Total",
|
||||
|
||||
@@ -13,6 +13,109 @@ export default class Bill extends mixin(TenantModel, [
|
||||
CustomViewBaseModel,
|
||||
ModelSearchable,
|
||||
]) {
|
||||
public amount: number;
|
||||
public paymentAmount: number;
|
||||
public landedCostAmount: number;
|
||||
public allocatedCostAmount: number;
|
||||
public isInclusiveTax: boolean;
|
||||
public taxAmountWithheld: number;
|
||||
public exchangeRate: number;
|
||||
|
||||
/**
|
||||
* Timestamps columns.
|
||||
*/
|
||||
get timestamps() {
|
||||
return ['createdAt', 'updatedAt'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Virtual attributes.
|
||||
*/
|
||||
static get virtualAttributes() {
|
||||
return [
|
||||
'balance',
|
||||
'dueAmount',
|
||||
'isOpen',
|
||||
'isPartiallyPaid',
|
||||
'isFullyPaid',
|
||||
'isPaid',
|
||||
'remainingDays',
|
||||
'overdueDays',
|
||||
'isOverdue',
|
||||
'unallocatedCostAmount',
|
||||
'localAmount',
|
||||
'localAllocatedCostAmount',
|
||||
'billableAmount',
|
||||
'amountLocal',
|
||||
'subtotal',
|
||||
'subtotalLocal',
|
||||
'subtotalExludingTax',
|
||||
'taxAmountWithheldLocal',
|
||||
'total',
|
||||
'totalLocal',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoice amount in base currency.
|
||||
* @returns {number}
|
||||
*/
|
||||
get amountLocal() {
|
||||
return this.amount * this.exchangeRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtotal. (Tax inclusive) if the tax inclusive is enabled.
|
||||
* @returns {number}
|
||||
*/
|
||||
get subtotal() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtotal in base currency. (Tax inclusive) if the tax inclusive is enabled.
|
||||
* @returns {number}
|
||||
*/
|
||||
get subtotalLocal() {
|
||||
return this.amountLocal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sale invoice amount excluding tax.
|
||||
* @returns {number}
|
||||
*/
|
||||
get subtotalExcludingTax() {
|
||||
return this.isInclusiveTax
|
||||
? this.subtotal - this.taxAmountWithheld
|
||||
: this.subtotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tax amount withheld in base currency.
|
||||
* @returns {number}
|
||||
*/
|
||||
get taxAmountWithheldLocal() {
|
||||
return this.taxAmountWithheld * this.exchangeRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoice total. (Tax included)
|
||||
* @returns {number}
|
||||
*/
|
||||
get total() {
|
||||
return this.isInclusiveTax
|
||||
? this.subtotal
|
||||
: this.subtotal + this.taxAmountWithheld;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoice total in local currency. (Tax included)
|
||||
* @returns {number}
|
||||
*/
|
||||
get totalLocal() {
|
||||
return this.total * this.exchangeRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
@@ -158,40 +261,13 @@ export default class Bill extends mixin(TenantModel, [
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Timestamps columns.
|
||||
*/
|
||||
get timestamps() {
|
||||
return ['createdAt', 'updatedAt'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Virtual attributes.
|
||||
*/
|
||||
static get virtualAttributes() {
|
||||
return [
|
||||
'balance',
|
||||
'dueAmount',
|
||||
'isOpen',
|
||||
'isPartiallyPaid',
|
||||
'isFullyPaid',
|
||||
'isPaid',
|
||||
'remainingDays',
|
||||
'overdueDays',
|
||||
'isOverdue',
|
||||
'unallocatedCostAmount',
|
||||
'localAmount',
|
||||
'localAllocatedCostAmount',
|
||||
'billableAmount',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoice amount in organization base currency.
|
||||
* @deprecated
|
||||
* @returns {number}
|
||||
*/
|
||||
get localAmount() {
|
||||
return this.amount * this.exchangeRate;
|
||||
return this.amountLocal;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,7 +307,7 @@ export default class Bill extends mixin(TenantModel, [
|
||||
* @return {number}
|
||||
*/
|
||||
get dueAmount() {
|
||||
return Math.max(this.amount - this.balance, 0);
|
||||
return Math.max(this.total - this.balance, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,7 +323,7 @@ export default class Bill extends mixin(TenantModel, [
|
||||
* @return {boolean}
|
||||
*/
|
||||
get isPartiallyPaid() {
|
||||
return this.dueAmount !== this.amount && this.dueAmount > 0;
|
||||
return this.dueAmount !== this.total && this.dueAmount > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,7 +384,7 @@ export default class Bill extends mixin(TenantModel, [
|
||||
* Retrieves the calculated amount which have not been invoiced.
|
||||
*/
|
||||
get billableAmount() {
|
||||
return Math.max(this.amount - this.invoicedAmount, 0);
|
||||
return Math.max(this.total - this.invoicedAmount, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,6 +402,7 @@ export default class Bill extends mixin(TenantModel, [
|
||||
const ItemEntry = require('models/ItemEntry');
|
||||
const BillLandedCost = require('models/BillLandedCost');
|
||||
const Branch = require('models/Branch');
|
||||
const TaxRateTransaction = require('models/TaxRateTransaction');
|
||||
|
||||
return {
|
||||
vendor: {
|
||||
@@ -373,6 +450,21 @@ export default class Bill extends mixin(TenantModel, [
|
||||
to: 'branches.id',
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Bill may has associated tax rate transactions.
|
||||
*/
|
||||
taxes: {
|
||||
relation: Model.HasManyRelation,
|
||||
modelClass: TaxRateTransaction.default,
|
||||
join: {
|
||||
from: 'bills.id',
|
||||
to: 'tax_rate_transactions.referenceId',
|
||||
},
|
||||
filter(builder) {
|
||||
builder.where('reference_type', 'Bill');
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ export default class Item extends mixin(TenantModel, [
|
||||
const ItemEntry = require('models/ItemEntry');
|
||||
const WarehouseTransferEntry = require('models/WarehouseTransferEntry');
|
||||
const InventoryAdjustmentEntry = require('models/InventoryAdjustmentEntry');
|
||||
const TaxRate = require('models/TaxRate');
|
||||
|
||||
return {
|
||||
/**
|
||||
@@ -178,11 +179,35 @@ export default class Item extends mixin(TenantModel, [
|
||||
to: 'media.id',
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Item may has sell tax rate.
|
||||
*/
|
||||
sellTaxRate: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: TaxRate.default,
|
||||
join: {
|
||||
from: 'items.sellTaxRateId',
|
||||
to: 'tax_rates.id',
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Item may has purchase tax rate.
|
||||
*/
|
||||
purchaseTaxRate: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: TaxRate.default,
|
||||
join: {
|
||||
from: 'items.purchaseTaxRateId',
|
||||
to: 'tax_rates.id',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
static get secureDeleteRelations() {
|
||||
return [
|
||||
|
||||
@@ -99,6 +99,13 @@ export default class ItemEntry extends TenantModel {
|
||||
: getExlusiveTaxAmount(this.amount, this.taxRate);
|
||||
}
|
||||
|
||||
static calcAmount(itemEntry) {
|
||||
const { discount, quantity, rate } = itemEntry;
|
||||
const total = quantity * rate;
|
||||
|
||||
return discount ? total - total * discount * 0.01 : total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Item entry relations.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import moment from 'moment';
|
||||
import { defaultTo, uniqBy } from 'lodash';
|
||||
import { defaultTo, sumBy, uniqBy } from 'lodash';
|
||||
import { IAccountTransaction, ILedger, ILedgerEntry } from '@/interfaces';
|
||||
|
||||
export default class Ledger implements ILedger {
|
||||
@@ -49,6 +49,15 @@ export default class Ledger implements ILedger {
|
||||
return this.filter((entry) => entry.accountId === accountId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters entries by the given accounts ids then returns a new ledger.
|
||||
* @param {number[]} accountIds
|
||||
* @returns {ILedger}
|
||||
*/
|
||||
public whereAccountsIds(accountIds: number[]): ILedger {
|
||||
return this.filter((entry) => accountIds.indexOf(entry.accountId) !== -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters entries that before or same the given date and returns a new ledger.
|
||||
* @param {Date|string} fromDate
|
||||
@@ -130,6 +139,22 @@ export default class Ledger implements ILedger {
|
||||
return closingBalance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the closing credit of the entries.
|
||||
* @returns {number}
|
||||
*/
|
||||
public getClosingCredit(): number {
|
||||
return sumBy(this.entries, 'credit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the closing debit of the entries.
|
||||
* @returns {number}
|
||||
*/
|
||||
public getClosingDebit(): number {
|
||||
return sumBy(this.entries, 'debit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the closing balance of the entries.
|
||||
* @returns {number}
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
import * as R from 'ramda';
|
||||
import { defaultTo, isEmpty, sumBy } from 'lodash';
|
||||
import FinancialSheet from '../FinancialSheet';
|
||||
import {
|
||||
IBalanceSheetAggregateNode,
|
||||
IBalanceSheetAccountNode,
|
||||
BALANCE_SHEET_SCHEMA_NODE_TYPE,
|
||||
IBalanceSheetQuery,
|
||||
INumberFormatQuery,
|
||||
IAccount,
|
||||
IBalanceSheetSchemaNode,
|
||||
IBalanceSheetSchemaAggregateNode,
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetSchemaAccountNode,
|
||||
IBalanceSheetCommonNode,
|
||||
} from '../../../interfaces';
|
||||
import { BalanceSheetSchema } from './BalanceSheetSchema';
|
||||
import { BalanceSheetPercentage } from './BalanceSheetPercentage';
|
||||
@@ -24,8 +16,14 @@ import { FinancialSheetStructure } from '../FinancialSheetStructure';
|
||||
import BalanceSheetRepository from './BalanceSheetRepository';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { BalanceSheetFiltering } from './BalanceSheetFiltering';
|
||||
import { BalanceSheetNetIncome } from './BalanceSheetNetIncome';
|
||||
import { BalanceSheetAggregators } from './BalanceSheetAggregators';
|
||||
import { BalanceSheetAccounts } from './BalanceSheetAccounts';
|
||||
|
||||
export default class BalanceSheet extends R.compose(
|
||||
BalanceSheetAggregators,
|
||||
BalanceSheetAccounts,
|
||||
BalanceSheetNetIncome,
|
||||
BalanceSheetFiltering,
|
||||
BalanceSheetDatePeriods,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
@@ -53,6 +51,9 @@ export default class BalanceSheet extends R.compose(
|
||||
*/
|
||||
readonly baseCurrency: string;
|
||||
|
||||
/**
|
||||
* Localization.
|
||||
*/
|
||||
readonly i18n: any;
|
||||
|
||||
/**
|
||||
@@ -77,216 +78,18 @@ export default class BalanceSheet extends R.compose(
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the accounts node of accounts types.
|
||||
* @param {string} accountsTypes
|
||||
* @returns {IAccount[]}
|
||||
* Parses report schema nodes.
|
||||
* @param {IBalanceSheetSchemaNode[]} schema
|
||||
* @returns {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
private getAccountsByAccountTypes = (accountsTypes: string[]): IAccount[] => {
|
||||
const mapAccountsByTypes = R.map((accountType) =>
|
||||
defaultTo(this.repository.accountsByType.get(accountType), [])
|
||||
);
|
||||
return R.compose(R.flatten, mapAccountsByTypes)(accountsTypes);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the aggregate schema node type.
|
||||
* @param {IBalanceSheetSchemaAggregateNode} node - Schema node.
|
||||
* @return {IBalanceSheetAggregateNode}
|
||||
*/
|
||||
private reportSchemaAggregateNodeMapper = (
|
||||
node: IBalanceSheetSchemaAggregateNode
|
||||
): IBalanceSheetAggregateNode => {
|
||||
const total = this.getTotalOfNodes(node.children);
|
||||
|
||||
return {
|
||||
name: this.i18n.__(node.name),
|
||||
id: node.id,
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
|
||||
total: this.getTotalAmountMeta(total),
|
||||
children: node.children,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose shema aggregate node of balance sheet schema.
|
||||
* @param {IBalanceSheetSchemaAggregateNode} node
|
||||
* @returns {IBalanceSheetSchemaAggregateNode}
|
||||
*/
|
||||
private schemaAggregateNodeCompose = (
|
||||
node: IBalanceSheetSchemaAggregateNode
|
||||
) => {
|
||||
return R.compose(
|
||||
this.aggregateNodeTotalMapper,
|
||||
this.reportSchemaAggregateNodeMapper
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the account model to report account node.
|
||||
* @param {IAccount} account
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private reportSchemaAccountNodeMapper = (
|
||||
account: IAccount
|
||||
): IBalanceSheetAccountNode => {
|
||||
const total = this.repository.totalAccountsLedger
|
||||
.whereAccountId(account.id)
|
||||
.getClosingBalance();
|
||||
|
||||
return {
|
||||
id: account.id,
|
||||
index: account.index,
|
||||
name: account.name,
|
||||
code: account.code,
|
||||
total: this.getAmountMeta(total),
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNT,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IAccount} account
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private reportSchemaAccountNodeComposer = (
|
||||
account: IAccount
|
||||
): IBalanceSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.previousYearAccountNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.previousPeriodAccountNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
this.assocAccountNodeDatePeriods
|
||||
),
|
||||
this.reportSchemaAccountNodeMapper
|
||||
)(account);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the total of the given nodes.
|
||||
* @param {IBalanceSheetCommonNode[]} nodes
|
||||
* @returns {number}
|
||||
*/
|
||||
private getTotalOfNodes = (nodes: IBalanceSheetCommonNode[]) => {
|
||||
return sumBy(nodes, 'total.amount');
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the report accounts node by the given accounts types.
|
||||
* @param {string[]} accountsTypes
|
||||
* @returns {}
|
||||
*/
|
||||
private getAccountsNodesByAccountTypes = (accountsTypes: string[]) => {
|
||||
const accounts = this.getAccountsByAccountTypes(accountsTypes);
|
||||
|
||||
return R.compose(R.map(this.reportSchemaAccountNodeComposer))(accounts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the accounts schema node type.
|
||||
* @param {IBalanceSheetSchemaNode} node - Schema node.
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private reportSchemaAccountsNodeMapper = (
|
||||
node: IBalanceSheetSchemaAccountNode
|
||||
): IBalanceSheetAccountNode => {
|
||||
const accounts = this.getAccountsNodesByAccountTypes(node.accountsTypes);
|
||||
const total = this.getTotalOfNodes(accounts);
|
||||
|
||||
return {
|
||||
id: node.id,
|
||||
name: this.i18n.__(node.name),
|
||||
type: node.type,
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
children: accounts,
|
||||
total: this.getTotalAmountMeta(total),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose account schema node to report node.
|
||||
* @param {IBalanceSheetSchemaAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private reportSchemaAccountsNodeComposer = (
|
||||
node: IBalanceSheetSchemaAccountNode
|
||||
): IBalanceSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.previousYearAggregateNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.previousPeriodAggregateNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
this.assocAccountsNodeDatePeriods
|
||||
),
|
||||
this.reportSchemaAccountsNodeMapper
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the given report schema node.
|
||||
* @param {IBalanceSheetSchemaNode} node - Schema node.
|
||||
* @return {IBalanceSheetDataNode}
|
||||
*/
|
||||
private reportSchemaNodeMapper = (
|
||||
schemaNode: IBalanceSheetSchemaNode
|
||||
): IBalanceSheetDataNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE),
|
||||
this.schemaAggregateNodeCompose
|
||||
),
|
||||
R.when(
|
||||
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS),
|
||||
this.reportSchemaAccountsNodeComposer
|
||||
)
|
||||
)(schemaNode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the report schema nodes.
|
||||
* @param {IBalanceSheetSchemaNode[]} nodes -
|
||||
* @return {IBalanceSheetStructureSection[]}
|
||||
*/
|
||||
private reportSchemaAccountNodesMapper = (
|
||||
schemaNodes: IBalanceSheetSchemaNode[]
|
||||
public parseSchemaNodes = (
|
||||
schema: IBalanceSheetSchemaNode[]
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return this.mapNodesDeepReverse(schemaNodes, this.reportSchemaNodeMapper);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets total amount that calculated from node children.
|
||||
* @param {IBalanceSheetSection} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
private aggregateNodeTotalMapper = (
|
||||
node: IBalanceSheetDataNode
|
||||
): IBalanceSheetDataNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.previousYearAggregateNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.previousPeriodAggregateNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
this.assocAggregateNodeDatePeriods
|
||||
)
|
||||
)(node);
|
||||
this.aggregatesSchemaParser,
|
||||
this.netIncomeSchemaParser,
|
||||
this.accountsSchemaParser
|
||||
)(schema) as IBalanceSheetDataNode[];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -299,7 +102,7 @@ export default class BalanceSheet extends R.compose(
|
||||
return R.compose(
|
||||
this.reportFilterPlugin,
|
||||
this.reportPercentageCompose,
|
||||
this.reportSchemaAccountNodesMapper
|
||||
this.parseSchemaNodes
|
||||
)(balanceSheetSchema);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
import * as R from 'ramda';
|
||||
import { defaultTo, toArray } from 'lodash';
|
||||
import { FinancialSheetStructure } from '../FinancialSheetStructure';
|
||||
import {
|
||||
BALANCE_SHEET_SCHEMA_NODE_TYPE,
|
||||
IAccount,
|
||||
IBalanceSheetAccountNode,
|
||||
IBalanceSheetAccountsNode,
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetSchemaAccountNode,
|
||||
IBalanceSheetSchemaNode,
|
||||
INumberFormatQuery,
|
||||
} from '@/interfaces';
|
||||
import { BalanceSheetNetIncome } from './BalanceSheetNetIncome';
|
||||
import { BalanceSheetFiltering } from './BalanceSheetFiltering';
|
||||
import { BalanceSheetDatePeriods } from './BalanceSheetDatePeriods';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
|
||||
import { BalanceSheetPercentage } from './BalanceSheetPercentage';
|
||||
import { BalanceSheetSchema } from './BalanceSheetSchema';
|
||||
import { BalanceSheetBase } from './BalanceSheetBase';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
|
||||
export const BalanceSheetAccounts = (Base: any) =>
|
||||
class extends R.compose(
|
||||
BalanceSheetNetIncome,
|
||||
BalanceSheetFiltering,
|
||||
BalanceSheetDatePeriods,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
BalanceSheetComparsionPreviousYear,
|
||||
BalanceSheetPercentage,
|
||||
BalanceSheetSchema,
|
||||
BalanceSheetBase,
|
||||
FinancialSheetStructure
|
||||
)(Base) {
|
||||
/**
|
||||
* Balance sheet query.
|
||||
* @param {BalanceSheetQuery}
|
||||
*/
|
||||
readonly query: BalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Balance sheet number format query.
|
||||
* @param {INumberFormatQuery}
|
||||
*/
|
||||
readonly numberFormat: INumberFormatQuery;
|
||||
|
||||
/**
|
||||
* Base currency of the organization.
|
||||
* @param {string}
|
||||
*/
|
||||
readonly baseCurrency: string;
|
||||
|
||||
/**
|
||||
* Localization.
|
||||
*/
|
||||
readonly i18n: any;
|
||||
|
||||
/**
|
||||
* Retrieve the accounts node of accounts types.
|
||||
* @param {string} accountsTypes
|
||||
* @returns {IAccount[]}
|
||||
*/
|
||||
private getAccountsByAccountTypes = (
|
||||
accountsTypes: string[]
|
||||
): IAccount[] => {
|
||||
const mapAccountsByTypes = R.map((accountType) =>
|
||||
defaultTo(this.repository.accountsByType.get(accountType), [])
|
||||
);
|
||||
return R.compose(R.flatten, mapAccountsByTypes)(accountsTypes);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the account model to report account node.
|
||||
* @param {IAccount} account
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private reportSchemaAccountNodeMapper = (
|
||||
account: IAccount
|
||||
): IBalanceSheetAccountNode => {
|
||||
const total = this.repository.totalAccountsLedger
|
||||
.whereAccountId(account.id)
|
||||
.getClosingBalance();
|
||||
|
||||
return {
|
||||
id: account.id,
|
||||
index: account.index,
|
||||
name: account.name,
|
||||
code: account.code,
|
||||
total: this.getAmountMeta(total),
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNT,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the given account model to the balance sheet account node.
|
||||
* @param {IAccount} account
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private reportSchemaAccountNodeComposer = (
|
||||
account: IAccount
|
||||
): IBalanceSheetAccountNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.previousYearAccountNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.previousPeriodAccountNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
this.assocAccountNodeDatePeriods
|
||||
),
|
||||
this.reportSchemaAccountNodeMapper
|
||||
)(account);
|
||||
};
|
||||
|
||||
// -----------------------------
|
||||
// - Accounts Node Praser
|
||||
// -----------------------------
|
||||
/**
|
||||
* Retrieve the report accounts node by the given accounts types.
|
||||
* @param {string[]} accountsTypes
|
||||
* @returns {IBalanceSheetAccountNode[]}
|
||||
*/
|
||||
private getAccountsNodesByAccountTypes = (
|
||||
accountsTypes: string[]
|
||||
): IBalanceSheetAccountNode[] => {
|
||||
const accounts = this.getAccountsByAccountTypes(accountsTypes);
|
||||
return R.map(this.reportSchemaAccountNodeComposer, accounts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the accounts schema node type.
|
||||
* @param {IBalanceSheetSchemaNode} node - Schema node.
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private reportSchemaAccountsNodeMapper = (
|
||||
node: IBalanceSheetSchemaAccountNode
|
||||
): IBalanceSheetAccountsNode => {
|
||||
const accounts = this.getAccountsNodesByAccountTypes(node.accountsTypes);
|
||||
const children = toArray(node?.children);
|
||||
|
||||
return {
|
||||
id: node.id,
|
||||
name: this.i18n.__(node.name),
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
children: [...accounts, ...children],
|
||||
total: this.getTotalAmountMeta(0),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the given report schema node.
|
||||
* @param {IBalanceSheetSchemaNode | IBalanceSheetDataNode} node - Schema node.
|
||||
* @return {IBalanceSheetSchemaNode | IBalanceSheetDataNode}
|
||||
*/
|
||||
private reportAccountSchemaParser = (
|
||||
node: IBalanceSheetSchemaNode | IBalanceSheetDataNode
|
||||
): IBalanceSheetSchemaNode | IBalanceSheetDataNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS),
|
||||
this.reportSchemaAccountsNodeMapper
|
||||
)
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses the report accounts schema nodes.
|
||||
* @param {IBalanceSheetSchemaNode[]} nodes -
|
||||
* @return {IBalanceSheetStructureSection[]}
|
||||
*/
|
||||
public accountsSchemaParser = (
|
||||
nodes: (IBalanceSheetSchemaNode | IBalanceSheetDataNode)[]
|
||||
): (IBalanceSheetDataNode | IBalanceSheetSchemaNode)[] => {
|
||||
return this.mapNodesDeepReverse(nodes, this.reportAccountSchemaParser);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,142 @@
|
||||
import * as R from 'ramda';
|
||||
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
|
||||
import { FinancialHorizTotals } from '../FinancialHorizTotals';
|
||||
import { FinancialSheetStructure } from '../FinancialSheetStructure';
|
||||
import {
|
||||
BALANCE_SHEET_SCHEMA_NODE_TYPE,
|
||||
IBalanceSheetAggregateNode,
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetSchemaAggregateNode,
|
||||
IBalanceSheetSchemaNode,
|
||||
INumberFormatQuery,
|
||||
} from '@/interfaces';
|
||||
import { BalanceSheetDatePeriods } from './BalanceSheetDatePeriods';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
|
||||
import { BalanceSheetPercentage } from './BalanceSheetPercentage';
|
||||
import { BalanceSheetSchema } from './BalanceSheetSchema';
|
||||
import { BalanceSheetBase } from './BalanceSheetBase';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
|
||||
export const BalanceSheetAggregators = (Base: any) =>
|
||||
class extends R.compose(
|
||||
BalanceSheetDatePeriods,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
BalanceSheetComparsionPreviousYear,
|
||||
BalanceSheetPercentage,
|
||||
BalanceSheetSchema,
|
||||
BalanceSheetBase,
|
||||
FinancialSheetStructure
|
||||
)(Base) {
|
||||
/**
|
||||
* Balance sheet query.
|
||||
* @param {BalanceSheetQuery}
|
||||
*/
|
||||
readonly query: BalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Balance sheet number format query.
|
||||
* @param {INumberFormatQuery}
|
||||
*/
|
||||
readonly numberFormat: INumberFormatQuery;
|
||||
|
||||
/**
|
||||
* Base currency of the organization.
|
||||
* @param {string}
|
||||
*/
|
||||
readonly baseCurrency: string;
|
||||
|
||||
/**
|
||||
* Localization.
|
||||
*/
|
||||
readonly i18n: any;
|
||||
|
||||
/**
|
||||
* Sets total amount that calculated from node children.
|
||||
* @param {IBalanceSheetSection} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
private aggregateNodeTotalMapper = (
|
||||
node: IBalanceSheetDataNode
|
||||
): IBalanceSheetDataNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.previousYearAggregateNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.previousPeriodAggregateNodeComposer
|
||||
),
|
||||
R.when(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
this.assocAggregateNodeDatePeriods
|
||||
)
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the aggregate schema node type.
|
||||
* @param {IBalanceSheetSchemaAggregateNode} node - Schema node.
|
||||
* @return {IBalanceSheetAggregateNode}
|
||||
*/
|
||||
private reportSchemaAggregateNodeMapper = (
|
||||
node: IBalanceSheetSchemaAggregateNode
|
||||
): IBalanceSheetAggregateNode => {
|
||||
const total = this.getTotalOfNodes(node.children);
|
||||
|
||||
return {
|
||||
name: this.i18n.__(node.name),
|
||||
id: node.id,
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE,
|
||||
total: this.getTotalAmountMeta(total),
|
||||
children: node.children,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Compose shema aggregate node of balance sheet schema.
|
||||
* @param {IBalanceSheetSchemaAggregateNode} node
|
||||
* @returns {IBalanceSheetSchemaAggregateNode}
|
||||
*/
|
||||
private schemaAggregateNodeCompose = (
|
||||
node: IBalanceSheetSchemaAggregateNode
|
||||
) => {
|
||||
return R.compose(
|
||||
this.aggregateNodeTotalMapper,
|
||||
this.reportSchemaAggregateNodeMapper
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the given report schema node.
|
||||
* @param {IBalanceSheetSchemaNode} node - Schema node.
|
||||
* @return {IBalanceSheetDataNode}
|
||||
*/
|
||||
private reportAggregateSchemaParser = (
|
||||
node: IBalanceSheetSchemaNode
|
||||
): IBalanceSheetDataNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.AGGREGATE),
|
||||
this.schemaAggregateNodeCompose
|
||||
),
|
||||
R.when(
|
||||
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS),
|
||||
this.schemaAggregateNodeCompose
|
||||
)
|
||||
)(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the report schema nodes.
|
||||
* @param {IBalanceSheetSchemaNode[]} nodes -
|
||||
* @return {IBalanceSheetStructureSection[]}
|
||||
*/
|
||||
public aggregatesSchemaParser = (
|
||||
nodes: (IBalanceSheetSchemaNode | IBalanceSheetDataNode)[]
|
||||
): (IBalanceSheetDataNode | IBalanceSheetSchemaNode)[] => {
|
||||
return this.mapNodesDeepReverse(nodes, this.reportAggregateSchemaParser);
|
||||
};
|
||||
};
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
IBalanceSheetAggregateNode,
|
||||
IBalanceSheetTotal,
|
||||
IBalanceSheetCommonNode,
|
||||
IBalanceSheetComparsions,
|
||||
} from '@/interfaces';
|
||||
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
|
||||
import { FinancialHorizTotals } from '../FinancialHorizTotals';
|
||||
|
||||
@@ -156,13 +156,14 @@ export const BalanceSheetComparsionPreviousYear = (Base: any) =>
|
||||
* @param {IBalanceSheetCommonNode} node
|
||||
* @returns {IBalanceSheetCommonNode}
|
||||
*/
|
||||
private assocPreviousYearAggregateHorizNode = (
|
||||
public assocPreviousYearAggregateHorizNode = (
|
||||
node: IBalanceSheetCommonNode
|
||||
) => {
|
||||
): IBalanceSheetCommonNode => {
|
||||
const horizontalTotals = R.addIndex(R.map)(
|
||||
this.previousYearAggregateHorizNodeComposer(node),
|
||||
node.horizontalTotals
|
||||
);
|
||||
) as IBalanceSheetTotal[];
|
||||
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
|
||||
@@ -258,12 +259,11 @@ export const BalanceSheetComparsionPreviousYear = (Base: any) =>
|
||||
// ------------------------------
|
||||
// # Horizontal Nodes - Aggregate.
|
||||
// ------------------------------
|
||||
|
||||
/**
|
||||
* Detarmines whether the given node has horizontal totals.
|
||||
* @param {IBalanceSheetCommonNode} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private isNodeHasHorizontalTotals = (node: IBalanceSheetCommonNode) =>
|
||||
public isNodeHasHorizontalTotals = (node: IBalanceSheetCommonNode) =>
|
||||
!isEmpty(node.horizontalTotals);
|
||||
};
|
||||
|
||||
@@ -107,9 +107,9 @@ export const BalanceSheetDatePeriods = (Base: FinancialSheet) =>
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @param {Date} fromDate
|
||||
* @param {Date} toDate
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @param {Date} fromDate
|
||||
* @param {Date} toDate
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
private getAccountNodeDatePeriod = (
|
||||
@@ -201,7 +201,7 @@ export const BalanceSheetDatePeriods = (Base: FinancialSheet) =>
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
BALANCE_SHEET_SCHEMA_NODE_TYPE,
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetNetIncomeNode,
|
||||
IBalanceSheetSchemaNetIncomeNode,
|
||||
IBalanceSheetSchemaNode,
|
||||
IBalanceSheetTotalPeriod,
|
||||
} from '@/interfaces';
|
||||
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
|
||||
import { FinancialHorizTotals } from '../FinancialHorizTotals';
|
||||
import BalanceSheetRepository from './BalanceSheetRepository';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { BalanceSheetNetIncomePP } from './BalanceSheetNetIncomePP';
|
||||
import { BalanceSheetNetIncomePY } from './BalanceSheetNetIncomePY';
|
||||
|
||||
export const BalanceSheetNetIncome = (Base: any) =>
|
||||
class extends R.compose(
|
||||
BalanceSheetNetIncomePP,
|
||||
BalanceSheetNetIncomePY,
|
||||
BalanceSheetComparsionPreviousYear,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
FinancialPreviousPeriod,
|
||||
FinancialHorizTotals
|
||||
)(Base) {
|
||||
private repository: BalanceSheetRepository;
|
||||
private query: BalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Retrieves the closing balance of income accounts.
|
||||
* @returns {number}
|
||||
*/
|
||||
private getIncomeTotal = () => {
|
||||
const closeingBalance = this.repository.incomeLedger.getClosingBalance();
|
||||
return closeingBalance;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the closing balance of expenses accounts.
|
||||
* @returns {number}
|
||||
*/
|
||||
private getExpensesTotal = () => {
|
||||
const closingBalance = this.repository.expensesLedger.getClosingBalance();
|
||||
return closingBalance;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the total net income.
|
||||
* @returns {number}
|
||||
*/
|
||||
protected getNetIncomeTotal = () => {
|
||||
const income = this.getIncomeTotal();
|
||||
const expenses = this.getExpensesTotal();
|
||||
|
||||
return income - expenses;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the aggregate schema node type.
|
||||
* @param {IBalanceSheetSchemaNetIncomeNode} node - Schema node.
|
||||
* @return {IBalanceSheetAggregateNode}
|
||||
*/
|
||||
protected schemaNetIncomeNodeMapper = (
|
||||
node: IBalanceSheetSchemaNetIncomeNode
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
const total = this.getNetIncomeTotal();
|
||||
|
||||
return {
|
||||
id: node.id,
|
||||
name: this.i18n.__(node.name),
|
||||
nodeType: BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME,
|
||||
total: this.getTotalAmountMeta(total),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Mapps the net income shcema node to report node.
|
||||
* @param {IBalanceSheetSchemaNetIncomeNode} node
|
||||
* @returns {IBalanceSheetNetIncomeNode}
|
||||
*/
|
||||
protected schemaNetIncomeNodeCompose = (
|
||||
node: IBalanceSheetSchemaNetIncomeNode
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.previousYearNetIncomeNodeCompose
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.previousPeriodNetIncomeNodeCompose
|
||||
),
|
||||
R.when(
|
||||
this.query.isDatePeriodsColumnsType,
|
||||
this.assocNetIncomeDatePeriodsNode
|
||||
),
|
||||
this.schemaNetIncomeNodeMapper
|
||||
)(node);
|
||||
};
|
||||
|
||||
// --------------------------------
|
||||
// # Date Periods
|
||||
// --------------------------------
|
||||
/**
|
||||
* Retreives total income of the given date period.
|
||||
* @param {number} accountId -
|
||||
* @param {Date} toDate -
|
||||
* @returns {number}
|
||||
*/
|
||||
private getIncomeDatePeriodTotal = (toDate: Date): number => {
|
||||
const periodTotalBetween = this.repository.incomePeriodsAccountsLedger
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const periodOpening =
|
||||
this.repository.incomePeriodsOpeningAccountsLedger.getClosingBalance();
|
||||
|
||||
return periodOpening + periodTotalBetween;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves total expense of the given date period.
|
||||
* @param {number} accountId -
|
||||
* @param {Date} toDate -
|
||||
* @returns {number}
|
||||
*/
|
||||
private getExpensesDatePeriodTotal = (toDate: Date): number => {
|
||||
const periodTotalBetween = this.repository.expensesPeriodsAccountsLedger
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const periodOpening =
|
||||
this.repository.expensesOpeningAccountLedger.getClosingBalance();
|
||||
|
||||
return periodOpening + periodTotalBetween;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the given net income date period total.
|
||||
* @param {number} accountId
|
||||
* @param {Date} toDate
|
||||
* @returns {number}
|
||||
*/
|
||||
private getNetIncomeDatePeriodTotal = (toDate: Date): number => {
|
||||
const income = this.getIncomeDatePeriodTotal(toDate);
|
||||
const expense = this.getExpensesDatePeriodTotal(toDate);
|
||||
|
||||
return income - expense;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the net income date period node.
|
||||
* @param {IBalanceSheetNetIncomeNode} node
|
||||
* @param {Date} fromDate
|
||||
* @param {Date} toDate
|
||||
* @returns {IBalanceSheetNetIncomeNode}
|
||||
*/
|
||||
private getNetIncomeDatePeriodNode = (
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
fromDate: Date,
|
||||
toDate: Date
|
||||
): IBalanceSheetTotalPeriod => {
|
||||
const periodTotal = this.getNetIncomeDatePeriodTotal(toDate);
|
||||
|
||||
return this.getDatePeriodTotalMeta(periodTotal, fromDate, toDate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve total date periods of the given net income node.
|
||||
* @param {IBalanceSheetNetIncomeNode} node
|
||||
* @returns {IBalanceSheetNetIncomeNode}
|
||||
*/
|
||||
private getNetIncomeDatePeriodsNode = (
|
||||
node: IBalanceSheetNetIncomeNode
|
||||
): IBalanceSheetTotalPeriod[] => {
|
||||
return this.getReportNodeDatePeriods(
|
||||
node,
|
||||
this.getNetIncomeDatePeriodNode
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc total date periods to net income node.
|
||||
* @param {IBalanceSheetNetIncomeNode} node
|
||||
* @returns {IBalanceSheetNetIncomeNode}
|
||||
*/
|
||||
public assocNetIncomeDatePeriodsNode = (
|
||||
node: IBalanceSheetNetIncomeNode
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
const datePeriods = this.getNetIncomeDatePeriodsNode(node);
|
||||
|
||||
return R.assoc('horizontalTotals', datePeriods, node);
|
||||
};
|
||||
|
||||
// -----------------------------
|
||||
// - Net Income Nodes Praser
|
||||
// -----------------------------
|
||||
/**
|
||||
* Mappes the given report schema node.
|
||||
* @param {IBalanceSheetSchemaNode} node - Schema node.
|
||||
* @return {IBalanceSheetDataNode}
|
||||
*/
|
||||
private reportNetIncomeNodeSchemaParser = (
|
||||
schemaNode: IBalanceSheetSchemaNode
|
||||
): IBalanceSheetDataNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isSchemaNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME),
|
||||
this.schemaNetIncomeNodeCompose
|
||||
)
|
||||
)(schemaNode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses the report net income schema nodes.
|
||||
* @param {(IBalanceSheetSchemaNode | IBalanceSheetDataNode)[]} nodes -
|
||||
* @return {IBalanceSheetDataNode[]}
|
||||
*/
|
||||
public netIncomeSchemaParser = (
|
||||
nodes: (IBalanceSheetSchemaNode | IBalanceSheetDataNode)[]
|
||||
): IBalanceSheetDataNode[] => {
|
||||
return this.mapNodesDeep(nodes, this.reportNetIncomeNodeSchemaParser);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,120 @@
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
IBalanceSheetNetIncomeNode,
|
||||
IBalanceSheetTotalPeriod,
|
||||
} from '@/interfaces';
|
||||
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
|
||||
import { FinancialHorizTotals } from '../FinancialHorizTotals';
|
||||
import BalanceSheetRepository from './BalanceSheetRepository';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { BalanceSheetNetIncomePP } from './BalanceSheetNetIncomePP';
|
||||
import { BalanceSheetNetIncomePY } from './BalanceSheetNetIncomePY';
|
||||
|
||||
export const BalanceSheetNetIncomeDatePeriods = (Base: any) =>
|
||||
class extends R.compose(
|
||||
BalanceSheetNetIncomePP,
|
||||
BalanceSheetNetIncomePY,
|
||||
BalanceSheetComparsionPreviousYear,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
FinancialPreviousPeriod,
|
||||
FinancialHorizTotals
|
||||
)(Base) {
|
||||
private repository: BalanceSheetRepository;
|
||||
private query: BalanceSheetQuery;
|
||||
|
||||
// --------------------------------
|
||||
// # Date Periods
|
||||
// --------------------------------
|
||||
/**
|
||||
* Retreives total income of the given date period.
|
||||
* @param {number} accountId -
|
||||
* @param {Date} toDate -
|
||||
* @returns {number}
|
||||
*/
|
||||
private getIncomeDatePeriodTotal = (toDate: Date): number => {
|
||||
const periodTotalBetween = this.repository.incomePeriodsAccountsLedger
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const periodOpening =
|
||||
this.repository.incomePeriodsOpeningAccountsLedger.getClosingBalance();
|
||||
|
||||
return periodOpening + periodTotalBetween;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves total expense of the given date period.
|
||||
* @param {number} accountId -
|
||||
* @param {Date} toDate -
|
||||
* @returns {number}
|
||||
*/
|
||||
private getExpensesDatePeriodTotal = (toDate: Date): number => {
|
||||
const periodTotalBetween = this.repository.expensesPeriodsAccountsLedger
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const periodOpening =
|
||||
this.repository.expensesOpeningAccountLedger.getClosingBalance();
|
||||
|
||||
return periodOpening + periodTotalBetween;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the given net income date period total.
|
||||
* @param {number} accountId
|
||||
* @param {Date} toDate
|
||||
* @returns {number}
|
||||
*/
|
||||
private getNetIncomeDatePeriodTotal = (toDate: Date): number => {
|
||||
const income = this.getIncomeDatePeriodTotal(toDate);
|
||||
const expense = this.getExpensesDatePeriodTotal(toDate);
|
||||
|
||||
return income - expense;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the net income date period node.
|
||||
* @param {IBalanceSheetNetIncomeNode} node
|
||||
* @param {Date} fromDate
|
||||
* @param {Date} toDate
|
||||
* @returns {IBalanceSheetNetIncomeNode}
|
||||
*/
|
||||
private getNetIncomeDatePeriodNode = (
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
fromDate: Date,
|
||||
toDate: Date
|
||||
): IBalanceSheetTotalPeriod => {
|
||||
const periodTotal = this.getNetIncomeDatePeriodTotal(toDate);
|
||||
|
||||
return this.getDatePeriodTotalMeta(periodTotal, fromDate, toDate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve total date periods of the given net income node.
|
||||
* @param {IBalanceSheetNetIncomeNode} node
|
||||
* @returns {IBalanceSheetNetIncomeNode}
|
||||
*/
|
||||
private getNetIncomeDatePeriodsNode = (
|
||||
node: IBalanceSheetNetIncomeNode
|
||||
): IBalanceSheetTotalPeriod[] => {
|
||||
return this.getReportNodeDatePeriods(
|
||||
node,
|
||||
this.getNetIncomeDatePeriodNode
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc total date periods to net income node.
|
||||
* @param {IBalanceSheetNetIncomeNode} node
|
||||
* @returns {IBalanceSheetNetIncomeNode}
|
||||
*/
|
||||
public assocNetIncomeDatePeriodsNode = (
|
||||
node: IBalanceSheetNetIncomeNode
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
const datePeriods = this.getNetIncomeDatePeriodsNode(node);
|
||||
|
||||
return R.assoc('horizontalTotals', datePeriods, node);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,127 @@
|
||||
import * as R from 'ramda';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
|
||||
import { FinancialHorizTotals } from '../FinancialHorizTotals';
|
||||
import { IBalanceSheetNetIncomeNode, IBalanceSheetTotal } from '@/interfaces';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import BalanceSheetRepository from './BalanceSheetRepository';
|
||||
|
||||
export const BalanceSheetNetIncomeDatePeriodsPP = (Base: any) =>
|
||||
class extends R.compose(
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
FinancialPreviousPeriod,
|
||||
FinancialHorizTotals
|
||||
)(Base) {
|
||||
query: BalanceSheetQuery;
|
||||
repository: BalanceSheetRepository;
|
||||
|
||||
/**
|
||||
* Retrieves the PY total income of the given date period.
|
||||
* @param {number} accountId -
|
||||
* @param {Date} toDate -
|
||||
* @return {number}
|
||||
*/
|
||||
private getPPIncomeDatePeriodTotal = R.curry((toDate: Date) => {
|
||||
const PYPeriodsTotal = this.repository.incomePPPeriodsAccountsLedger
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const PYPeriodsOpeningTotal =
|
||||
this.repository.incomePPPeriodsOpeningAccountLedger.getClosingBalance();
|
||||
|
||||
return PYPeriodsOpeningTotal + PYPeriodsTotal;
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieves the PY total expense of the given date period.
|
||||
* @param {number} accountId -
|
||||
* @param {Date} toDate -
|
||||
* @returns {number}
|
||||
*/
|
||||
private getPPExpenseDatePeriodTotal = R.curry((toDate: Date) => {
|
||||
const PYPeriodsTotal = this.repository.expensePPPeriodsAccountsLedger
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const PYPeriodsOpeningTotal =
|
||||
this.repository.expensePPPeriodsOpeningAccountLedger.getClosingBalance();
|
||||
|
||||
return PYPeriodsOpeningTotal + PYPeriodsTotal;
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieve the given net income total of the given period.
|
||||
* @param {number} accountId - Account id.
|
||||
* @param {Date} toDate - To date.
|
||||
* @returns {number}
|
||||
*/
|
||||
private getPPNetIncomeDatePeriodTotal = R.curry((toDate: Date) => {
|
||||
const income = this.getPPIncomeDatePeriodTotal(toDate);
|
||||
const expense = this.getPPExpenseDatePeriodTotal(toDate);
|
||||
|
||||
return income - expense;
|
||||
});
|
||||
|
||||
/**
|
||||
* Assoc preivous period to account horizontal total node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {}
|
||||
*/
|
||||
private assocPreviousPeriodNetIncomeHorizTotal = R.curry(
|
||||
(node: IBalanceSheetNetIncomeNode, totalNode) => {
|
||||
const total = this.getPPNetIncomeDatePeriodTotal(
|
||||
totalNode.previousPeriodToDate.date
|
||||
);
|
||||
return R.assoc('previousPeriod', this.getAmountMeta(total), totalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Compose previous period to aggregate horizontal nodes.
|
||||
* @param {IBalanceSheetTotal} node
|
||||
* @returns {IBalanceSheetTotal}
|
||||
*/
|
||||
private previousPeriodNetIncomeHorizNodeComposer = R.curry(
|
||||
(
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
horiontalTotalNode: IBalanceSheetTotal
|
||||
): IBalanceSheetTotal => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodTotalPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodTotalChangeNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodNetIncomeHorizTotal(node)
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodActive,
|
||||
this.assocPreviousPeriodHorizNodeFromToDates(
|
||||
this.query.displayColumnsBy
|
||||
)
|
||||
)
|
||||
)(horiontalTotalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Associate the PP to net income horizontal nodes.
|
||||
* @param {IBalanceSheetCommonNode} node
|
||||
* @returns {IBalanceSheetCommonNode}
|
||||
*/
|
||||
public assocPreviousPeriodNetIncomeHorizNode = (
|
||||
node: IBalanceSheetNetIncomeNode
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
const horizontalTotals = R.addIndex(R.map)(
|
||||
this.previousPeriodNetIncomeHorizNodeComposer(node),
|
||||
node.horizontalTotals
|
||||
) as IBalanceSheetTotal[];
|
||||
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,122 @@
|
||||
import * as R from 'ramda';
|
||||
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
|
||||
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
|
||||
import { FinancialHorizTotals } from '../FinancialHorizTotals';
|
||||
import { IBalanceSheetNetIncomeNode, IBalanceSheetTotal } from '@/interfaces';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import BalanceSheetRepository from './BalanceSheetRepository';
|
||||
|
||||
export const BalanceSheetNetIncomeDatePeriodsPY = (Base: any) =>
|
||||
class extends R.compose(
|
||||
BalanceSheetComparsionPreviousYear,
|
||||
FinancialPreviousPeriod,
|
||||
FinancialHorizTotals
|
||||
)(Base) {
|
||||
query: BalanceSheetQuery;
|
||||
repository: BalanceSheetRepository;
|
||||
|
||||
/**
|
||||
* Retrieves the PY total income of the given date period.
|
||||
* @param {Date} toDate -
|
||||
* @return {number}
|
||||
*/
|
||||
private getPYIncomeDatePeriodTotal = R.curry((toDate: Date) => {
|
||||
const PYPeriodsTotal = this.repository.incomePYPeriodsAccountsLedger
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const PYPeriodsOpeningTotal =
|
||||
this.repository.incomePYPeriodsOpeningAccountLedger.getClosingBalance();
|
||||
|
||||
return PYPeriodsOpeningTotal + PYPeriodsTotal;
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieves the PY total expense of the given date period.
|
||||
* @param {Date} toDate -
|
||||
* @returns {number}
|
||||
*/
|
||||
private getPYExpenseDatePeriodTotal = R.curry((toDate: Date) => {
|
||||
const PYPeriodsTotal = this.repository.expensePYPeriodsAccountsLedger
|
||||
.whereToDate(toDate)
|
||||
.getClosingBalance();
|
||||
|
||||
const PYPeriodsOpeningTotal =
|
||||
this.repository.expensePYPeriodsOpeningAccountLedger.getClosingBalance();
|
||||
|
||||
return PYPeriodsOpeningTotal + PYPeriodsTotal;
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieve the given net income total of the given period.
|
||||
* @param {Date} toDate - To date.
|
||||
* @returns {number}
|
||||
*/
|
||||
private getPYNetIncomeDatePeriodTotal = R.curry((toDate: Date) => {
|
||||
const income = this.getPYIncomeDatePeriodTotal(toDate);
|
||||
const expense = this.getPYExpenseDatePeriodTotal(toDate);
|
||||
|
||||
return income - expense;
|
||||
});
|
||||
|
||||
/**
|
||||
* Assoc preivous year to account horizontal total node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {}
|
||||
*/
|
||||
private assocPreviousYearNetIncomeHorizTotal = R.curry(
|
||||
(node: IBalanceSheetNetIncomeNode, totalNode) => {
|
||||
const total = this.getPYNetIncomeDatePeriodTotal(
|
||||
totalNode.previousYearToDate.date
|
||||
);
|
||||
return R.assoc('previousYear', this.getAmountMeta(total), totalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Compose PY to net income horizontal nodes.
|
||||
* @param {IBalanceSheetTotal} node
|
||||
* @returns {IBalanceSheetTotal}
|
||||
*/
|
||||
private previousYearNetIncomeHorizNodeComposer = R.curry(
|
||||
(
|
||||
node: IBalanceSheetNetIncomeNode,
|
||||
horiontalTotalNode: IBalanceSheetTotal
|
||||
): IBalanceSheetTotal => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearTotalPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearTotalChangeNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.assocPreviousYearNetIncomeHorizTotal(node)
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearActive,
|
||||
this.assocPreviousYearHorizNodeFromToDates
|
||||
)
|
||||
)(horiontalTotalNode);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Associate the PY to net income horizontal nodes.
|
||||
* @param {IBalanceSheetCommonNode} node
|
||||
* @returns {IBalanceSheetCommonNode}
|
||||
*/
|
||||
public assocPreviousYearNetIncomeHorizNode = (
|
||||
node: IBalanceSheetNetIncomeNode
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
const horizontalTotals = R.addIndex(R.map)(
|
||||
this.previousYearNetIncomeHorizNodeComposer(node),
|
||||
node.horizontalTotals
|
||||
) as IBalanceSheetTotal[];
|
||||
|
||||
return R.assoc('horizontalTotals', horizontalTotals, node);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetNetIncomeNode,
|
||||
} from '@/interfaces';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
|
||||
import { FinancialHorizTotals } from '../FinancialHorizTotals';
|
||||
import BalanceSheetRepository from './BalanceSheetRepository';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { BalanceSheetNetIncomeDatePeriodsPP } from './BalanceSheetNetIncomeDatePeriodsPP';
|
||||
|
||||
export const BalanceSheetNetIncomePP = (Base: any) =>
|
||||
class extends R.compose(
|
||||
BalanceSheetNetIncomeDatePeriodsPP,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
FinancialPreviousPeriod,
|
||||
FinancialHorizTotals
|
||||
)(Base) {
|
||||
private repository: BalanceSheetRepository;
|
||||
private query: BalanceSheetQuery;
|
||||
|
||||
// -------------------------------
|
||||
// # Previous Period (PP)
|
||||
// -------------------------------
|
||||
/**
|
||||
* Retrieves the PP net income.
|
||||
* @returns {}
|
||||
*/
|
||||
protected getPreviousPeriodNetIncome = () => {
|
||||
const income = this.repository.incomePPAccountsLedger.getClosingBalance();
|
||||
const expense =
|
||||
this.repository.expensePPAccountsLedger.getClosingBalance();
|
||||
|
||||
return income - expense;
|
||||
};
|
||||
|
||||
/**
|
||||
* Associates the previous period to account node.
|
||||
* @param {IBalanceSheetDataNode} node
|
||||
* @returns {IBalanceSheetDataNode}
|
||||
*/
|
||||
protected assocPreviousPeriodNetIncomeNode = (
|
||||
node: IBalanceSheetDataNode
|
||||
): IBalanceSheetDataNode => {
|
||||
const total = this.getPreviousPeriodNetIncome();
|
||||
|
||||
return R.assoc('previousPeriod', this.getAmountMeta(total), node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Previous period account node composer.
|
||||
* @param {IBalanceSheetNetIncomeNode} node
|
||||
* @returns {IBalanceSheetNetIncomeNode}
|
||||
*/
|
||||
protected previousPeriodNetIncomeNodeCompose = (
|
||||
node: IBalanceSheetNetIncomeNode
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.isNodeHasHorizTotals,
|
||||
this.assocPreviousPeriodNetIncomeHorizNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodPercentageActive,
|
||||
this.assocPreviousPeriodPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousPeriodChangeActive,
|
||||
this.assocPreviousPeriodChangeNode
|
||||
),
|
||||
this.assocPreviousPeriodNetIncomeNode
|
||||
)(node);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetNetIncomeNode,
|
||||
} from '@/interfaces';
|
||||
import { BalanceSheetComparsionPreviousYear } from './BalanceSheetComparsionPreviousYear';
|
||||
import { BalanceSheetComparsionPreviousPeriod } from './BalanceSheetComparsionPreviousPeriod';
|
||||
import { FinancialPreviousPeriod } from '../FinancialPreviousPeriod';
|
||||
import { FinancialHorizTotals } from '../FinancialHorizTotals';
|
||||
import BalanceSheetRepository from './BalanceSheetRepository';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { BalanceSheetNetIncomeDatePeriodsPY } from './BalanceSheetNetIncomeDatePeriodsPY';
|
||||
|
||||
export const BalanceSheetNetIncomePY = (Base: any) =>
|
||||
class extends R.compose(
|
||||
BalanceSheetNetIncomeDatePeriodsPY,
|
||||
BalanceSheetComparsionPreviousYear,
|
||||
BalanceSheetComparsionPreviousPeriod,
|
||||
FinancialPreviousPeriod,
|
||||
FinancialHorizTotals
|
||||
)(Base) {
|
||||
private repository: BalanceSheetRepository;
|
||||
private query: BalanceSheetQuery;
|
||||
|
||||
// ------------------------------
|
||||
// # Previous Year (PY)
|
||||
// ------------------------------
|
||||
/**
|
||||
* Retrieves the previous year (PY) net income.
|
||||
* @returns {number}
|
||||
*/
|
||||
protected getPreviousYearNetIncome = () => {
|
||||
const income =
|
||||
this.repository.incomePYTotalAccountsLedger.getClosingBalance();
|
||||
const expense =
|
||||
this.repository.expensePYTotalAccountsLedger.getClosingBalance();
|
||||
|
||||
return income - expense;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc previous year on aggregate node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
protected assocPreviousYearNetIncomeNode = (
|
||||
node: IBalanceSheetNetIncomeNode
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
const total = this.getPreviousYearNetIncome();
|
||||
|
||||
return R.assoc('previousYear', this.getTotalAmountMeta(total), node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assoc previous year attributes to aggregate node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {IBalanceSheetAccountNode}
|
||||
*/
|
||||
protected previousYearNetIncomeNodeCompose = (
|
||||
node: IBalanceSheetNetIncomeNode
|
||||
): IBalanceSheetNetIncomeNode => {
|
||||
return R.compose(
|
||||
R.when(
|
||||
this.query.isPreviousYearPercentageActive,
|
||||
this.assocPreviousYearTotalPercentageNode
|
||||
),
|
||||
R.when(
|
||||
this.query.isPreviousYearChangeActive,
|
||||
this.assocPreviousYearTotalChangeNode
|
||||
),
|
||||
// Associate the PY to date periods horizontal nodes.
|
||||
R.when(
|
||||
this.isNodeHasHorizontalTotals,
|
||||
this.assocPreviousYearNetIncomeHorizNode
|
||||
),
|
||||
this.assocPreviousYearNetIncomeNode
|
||||
)(node);
|
||||
};
|
||||
};
|
||||
@@ -12,26 +12,31 @@ export class BalanceSheetQuery extends R.compose(FinancialDateRanges)(
|
||||
* @param {IBalanceSheetQuery}
|
||||
*/
|
||||
public readonly query: IBalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Previous year to date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PYToDate: Date;
|
||||
|
||||
/**
|
||||
* Previous year from date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PYFromDate: Date;
|
||||
|
||||
/**
|
||||
* Previous period to date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PPToDate: Date;
|
||||
|
||||
/**
|
||||
* Previous period from date.
|
||||
* @param {Date}
|
||||
*/
|
||||
public readonly PPFromDate: Date;
|
||||
|
||||
/**
|
||||
* Constructor method
|
||||
* @param {IBalanceSheetQuery} query
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as R from 'ramda';
|
||||
import { Knex } from 'knex';
|
||||
import { isEmpty } from 'lodash';
|
||||
import {
|
||||
IAccount,
|
||||
IAccountTransactionsGroupBy,
|
||||
IBalanceSheetQuery,
|
||||
ILedger,
|
||||
@@ -11,9 +12,12 @@ import { transformToMapBy } from 'utils';
|
||||
import Ledger from '@/services/Accounting/Ledger';
|
||||
import { BalanceSheetQuery } from './BalanceSheetQuery';
|
||||
import { FinancialDatePeriods } from '../FinancialDatePeriods';
|
||||
import { ACCOUNT_PARENT_TYPE, ACCOUNT_TYPE } from '@/data/AccountTypes';
|
||||
import { BalanceSheetRepositoryNetIncome } from './BalanceSheetRepositoryNetIncome';
|
||||
|
||||
@Service()
|
||||
export default class BalanceSheetRepository extends R.compose(
|
||||
BalanceSheetRepositoryNetIncome,
|
||||
FinancialDatePeriods
|
||||
)(class {}) {
|
||||
/**
|
||||
@@ -65,8 +69,22 @@ export default class BalanceSheetRepository extends R.compose(
|
||||
*/
|
||||
public readonly PPFromDate: Date;
|
||||
|
||||
/**
|
||||
* Total closing accounts ledger.
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public totalAccountsLedger: Ledger;
|
||||
|
||||
/**
|
||||
* Total income accounts ledger.
|
||||
*/
|
||||
public incomeLedger: Ledger;
|
||||
|
||||
/**
|
||||
* Total expense accounts ledger.
|
||||
*/
|
||||
public expensesLedger: Ledger;
|
||||
|
||||
/**
|
||||
* Transactions group type.
|
||||
* @param {IAccountTransactionsGroupBy}
|
||||
@@ -171,6 +189,8 @@ export default class BalanceSheetRepository extends R.compose(
|
||||
) {
|
||||
await this.initPeriodsPreviousPeriod();
|
||||
}
|
||||
//
|
||||
await this.asyncInitializeNetIncome();
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
@@ -181,6 +201,7 @@ export default class BalanceSheetRepository extends R.compose(
|
||||
|
||||
this.accounts = accounts;
|
||||
this.accountsByType = transformToMapBy(accounts, 'accountType');
|
||||
this.accountsByParentType = transformToMapBy(accounts, 'accountParentType');
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
@@ -309,14 +330,15 @@ export default class BalanceSheetRepository extends R.compose(
|
||||
|
||||
/**
|
||||
* Closing accounts date periods.
|
||||
* @param openingDate
|
||||
* @param datePeriodsType
|
||||
* @param {Date} fromDate
|
||||
* @param {Date} toDate
|
||||
* @param {string} datePeriodsType
|
||||
* @returns
|
||||
*/
|
||||
public accountsDatePeriods = async (
|
||||
fromDate: Date,
|
||||
toDate: Date,
|
||||
datePeriodsType
|
||||
datePeriodsType: string
|
||||
) => {
|
||||
const { AccountTransaction } = this.models;
|
||||
|
||||
@@ -336,6 +358,7 @@ export default class BalanceSheetRepository extends R.compose(
|
||||
|
||||
/**
|
||||
* Retrieve the opening balance transactions of the report.
|
||||
* @param {Date|string} openingDate -
|
||||
*/
|
||||
public closingAccountsTotal = async (openingDate: Date | string) => {
|
||||
const { AccountTransaction } = this.models;
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
import * as R from 'ramda';
|
||||
import { IAccount, ILedger } from '@/interfaces';
|
||||
import { FinancialDatePeriods } from '../FinancialDatePeriods';
|
||||
import { ACCOUNT_PARENT_TYPE } from '@/data/AccountTypes';
|
||||
|
||||
export const BalanceSheetRepositoryNetIncome = (Base) =>
|
||||
class extends R.compose(FinancialDatePeriods)(Base) {
|
||||
// -----------------------
|
||||
// # Net Income
|
||||
// -----------------------
|
||||
public incomeAccounts: IAccount[];
|
||||
public incomeAccountsIds: number[];
|
||||
|
||||
public expenseAccounts: IAccount[];
|
||||
public expenseAccountsIds: number[];
|
||||
|
||||
public incomePeriodsAccountsLedger: ILedger;
|
||||
public incomePeriodsOpeningAccountsLedger: ILedger;
|
||||
public expensesPeriodsAccountsLedger: ILedger;
|
||||
public expensesOpeningAccountLedger: ILedger;
|
||||
|
||||
public incomePPAccountsLedger: ILedger;
|
||||
public expensePPAccountsLedger: ILedger;
|
||||
|
||||
public incomePPPeriodsAccountsLedger: ILedger;
|
||||
public incomePPPeriodsOpeningAccountLedger: ILedger;
|
||||
public expensePPPeriodsAccountsLedger: ILedger;
|
||||
public expensePPPeriodsOpeningAccountLedger: ILedger;
|
||||
|
||||
public incomePYTotalAccountsLedger: ILedger;
|
||||
public expensePYTotalAccountsLedger: ILedger;
|
||||
|
||||
public incomePYPeriodsAccountsLedger: ILedger;
|
||||
public incomePYPeriodsOpeningAccountLedger: ILedger;
|
||||
public expensePYPeriodsAccountsLedger: ILedger;
|
||||
public expensePYPeriodsOpeningAccountLedger: ILedger;
|
||||
|
||||
/**
|
||||
* Async initialize.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public asyncInitializeNetIncome = async () => {
|
||||
await this.initAccounts();
|
||||
await this.initAccountsTotalLedger();
|
||||
|
||||
// Net Income
|
||||
this.initIncomeAccounts();
|
||||
this.initExpenseAccounts();
|
||||
|
||||
this.initIncomeTotalLedger();
|
||||
this.initExpensesTotalLedger();
|
||||
|
||||
// Date periods
|
||||
if (this.query.isDatePeriodsColumnsType()) {
|
||||
this.initNetIncomeDatePeriods();
|
||||
}
|
||||
// Previous Year (PY).
|
||||
if (this.query.isPreviousYearActive()) {
|
||||
this.initNetIncomePreviousYear();
|
||||
}
|
||||
// Previous Period (PP).
|
||||
if (this.query.isPreviousPeriodActive()) {
|
||||
this.initNetIncomePreviousPeriod();
|
||||
}
|
||||
// Previous Year (PY) / Date Periods.
|
||||
if (
|
||||
this.query.isPreviousYearActive() &&
|
||||
this.query.isDatePeriodsColumnsType()
|
||||
) {
|
||||
this.initNetIncomePeriodsPreviewYear();
|
||||
}
|
||||
// Previous Period (PP) / Date Periods.
|
||||
if (
|
||||
this.query.isPreviousPeriodActive() &&
|
||||
this.query.isDatePeriodsColumnsType()
|
||||
) {
|
||||
this.initNetIncomePeriodsPreviousPeriod();
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Net Income
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize income accounts.
|
||||
*/
|
||||
private initIncomeAccounts = () => {
|
||||
const incomeAccounts = this.accountsByParentType.get(
|
||||
ACCOUNT_PARENT_TYPE.INCOME
|
||||
);
|
||||
const incomeAccountsIds = incomeAccounts.map((a) => a.id);
|
||||
|
||||
this.incomeAccounts = incomeAccounts;
|
||||
this.incomeAccountsIds = incomeAccountsIds;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize expense accounts.
|
||||
*/
|
||||
private initExpenseAccounts = () => {
|
||||
const expensesAccounts = this.accountsByParentType.get(
|
||||
ACCOUNT_PARENT_TYPE.EXPENSE
|
||||
);
|
||||
const expensesAccountsIds = expensesAccounts.map((a) => a.id);
|
||||
|
||||
this.expenseAccounts = expensesAccounts;
|
||||
this.expenseAccountsIds = expensesAccountsIds;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the income total ledger.
|
||||
*/
|
||||
private initIncomeTotalLedger = (): void => {
|
||||
// Inject to the repository.
|
||||
this.incomeLedger = this.totalAccountsLedger.whereAccountsIds(
|
||||
this.incomeAccountsIds
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the expenses total ledger.
|
||||
*/
|
||||
private initExpensesTotalLedger = (): void => {
|
||||
this.expensesLedger = this.totalAccountsLedger.whereAccountsIds(
|
||||
this.expenseAccountsIds
|
||||
);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Net Income - Date Periods
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize the net income date periods.
|
||||
*/
|
||||
public initNetIncomeDatePeriods = () => {
|
||||
this.incomePeriodsAccountsLedger =
|
||||
this.periodsAccountsLedger.whereAccountsIds(this.incomeAccountsIds);
|
||||
|
||||
this.incomePeriodsOpeningAccountsLedger =
|
||||
this.periodsOpeningAccountLedger.whereAccountsIds(
|
||||
this.incomeAccountsIds
|
||||
);
|
||||
|
||||
this.expensesPeriodsAccountsLedger =
|
||||
this.periodsAccountsLedger.whereAccountsIds(this.expenseAccountsIds);
|
||||
|
||||
this.expensesOpeningAccountLedger =
|
||||
this.periodsOpeningAccountLedger.whereAccountsIds(
|
||||
this.expenseAccountsIds
|
||||
);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Net Income - Previous Period
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize the total net income PP.
|
||||
*/
|
||||
public initNetIncomePreviousPeriod = () => {
|
||||
this.incomePPAccountsLedger = this.PPTotalAccountsLedger.whereAccountsIds(
|
||||
this.incomeAccountsIds
|
||||
);
|
||||
this.expensePPAccountsLedger =
|
||||
this.PPTotalAccountsLedger.whereAccountsIds(this.expenseAccountsIds);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the net income periods of previous period.
|
||||
*/
|
||||
public initNetIncomePeriodsPreviousPeriod = () => {
|
||||
this.incomePPPeriodsAccountsLedger =
|
||||
this.PPPeriodsAccountsLedger.whereAccountsIds(this.incomeAccountsIds);
|
||||
|
||||
this.incomePPPeriodsOpeningAccountLedger =
|
||||
this.PPPeriodsOpeningAccountLedger.whereAccountsIds(
|
||||
this.incomeAccountsIds
|
||||
);
|
||||
|
||||
this.expensePPPeriodsAccountsLedger =
|
||||
this.PPPeriodsAccountsLedger.whereAccountsIds(this.expenseAccountsIds);
|
||||
|
||||
this.expensePPPeriodsOpeningAccountLedger =
|
||||
this.PPPeriodsOpeningAccountLedger.whereAccountsIds(
|
||||
this.expenseAccountsIds
|
||||
);
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Net Income - Previous Year
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize the net income PY total.
|
||||
*/
|
||||
public initNetIncomePreviousYear = () => {
|
||||
this.incomePYTotalAccountsLedger =
|
||||
this.PYTotalAccountsLedger.whereAccountsIds(this.incomeAccountsIds);
|
||||
|
||||
this.expensePYTotalAccountsLedger =
|
||||
this.PYTotalAccountsLedger.whereAccountsIds(this.expenseAccountsIds);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the net income PY periods.
|
||||
*/
|
||||
public initNetIncomePeriodsPreviewYear = () => {
|
||||
this.incomePYPeriodsAccountsLedger =
|
||||
this.PYPeriodsAccountsLedger.whereAccountsIds(this.incomeAccountsIds);
|
||||
|
||||
this.incomePYPeriodsOpeningAccountLedger =
|
||||
this.PYPeriodsOpeningAccountLedger.whereAccountsIds(
|
||||
this.incomeAccountsIds
|
||||
);
|
||||
|
||||
this.expensePYPeriodsAccountsLedger =
|
||||
this.PYPeriodsAccountsLedger.whereAccountsIds(this.expenseAccountsIds);
|
||||
|
||||
this.expensePYPeriodsOpeningAccountLedger =
|
||||
this.PYPeriodsOpeningAccountLedger.whereAccountsIds(
|
||||
this.expenseAccountsIds
|
||||
);
|
||||
};
|
||||
};
|
||||
@@ -7,12 +7,11 @@ import {
|
||||
import { ACCOUNT_TYPE } from '@/data/AccountTypes';
|
||||
import { FinancialSchema } from '../FinancialSchema';
|
||||
|
||||
|
||||
export const BalanceSheetSchema = (Base) =>
|
||||
class extends R.compose(FinancialSchema)(Base) {
|
||||
/**
|
||||
* Retrieves the balance sheet schema.
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
getSchema = () => {
|
||||
return getBalanceSheetSchema();
|
||||
@@ -115,6 +114,13 @@ export const getBalanceSheetSchema = () => [
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.EQUITY,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNTS,
|
||||
accountsTypes: [ACCOUNT_TYPE.EQUITY],
|
||||
children: [
|
||||
{
|
||||
name: 'balance_sheet.net_income',
|
||||
id: BALANCE_SHEET_SCHEMA_NODE_ID.NET_INCOME,
|
||||
type: BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
alwaysShow: true,
|
||||
|
||||
@@ -8,6 +8,10 @@ import {
|
||||
BALANCE_SHEET_SCHEMA_NODE_TYPE,
|
||||
IBalanceSheetDataNode,
|
||||
IBalanceSheetSchemaNode,
|
||||
IBalanceSheetNetIncomeNode,
|
||||
IBalanceSheetAccountNode,
|
||||
IBalanceSheetAccountsNode,
|
||||
IBalanceSheetAggregateNode,
|
||||
} from '@/interfaces';
|
||||
import { tableRowMapper } from 'utils';
|
||||
import FinancialSheet from '../FinancialSheet';
|
||||
@@ -108,11 +112,13 @@ export default class BalanceSheetTable extends R.compose(
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* Retrieves the table row from the given report aggregate node.
|
||||
* @param {IBalanceSheetAggregateNode} node
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
private aggregateNodeTableRowsMapper = (node): ITableRow => {
|
||||
private aggregateNodeTableRowsMapper = (
|
||||
node: IBalanceSheetAggregateNode
|
||||
): ITableRow => {
|
||||
const columns = this.commonColumnsAccessors();
|
||||
const meta = {
|
||||
rowTypes: [IROW_TYPE.AGGREGATE],
|
||||
@@ -122,11 +128,13 @@ export default class BalanceSheetTable extends R.compose(
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* Retrieves the table row from the given report accounts node.
|
||||
* @param {IBalanceSheetAccountsNode} node
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
private accountsNodeTableRowsMapper = (node): ITableRow => {
|
||||
private accountsNodeTableRowsMapper = (
|
||||
node: IBalanceSheetAccountsNode
|
||||
): ITableRow => {
|
||||
const columns = this.commonColumnsAccessors();
|
||||
const meta = {
|
||||
rowTypes: [IROW_TYPE.ACCOUNTS],
|
||||
@@ -136,11 +144,13 @@ export default class BalanceSheetTable extends R.compose(
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {} node
|
||||
* Retrieves the table row from the given report account node.
|
||||
* @param {IBalanceSheetAccountNode} node
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
private accountNodeTableRowsMapper = (node): ITableRow => {
|
||||
private accountNodeTableRowsMapper = (
|
||||
node: IBalanceSheetAccountNode
|
||||
): ITableRow => {
|
||||
const columns = this.commonColumnsAccessors();
|
||||
|
||||
const meta = {
|
||||
@@ -150,6 +160,22 @@ export default class BalanceSheetTable extends R.compose(
|
||||
return tableRowMapper(node, columns, meta);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the table row from the given report net income node.
|
||||
* @param {IBalanceSheetNetIncomeNode} node
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
private netIncomeNodeTableRowsMapper = (
|
||||
node: IBalanceSheetNetIncomeNode
|
||||
): ITableRow => {
|
||||
const columns = this.commonColumnsAccessors();
|
||||
const meta = {
|
||||
rowTypes: [IROW_TYPE.NET_INCOME],
|
||||
id: node.id,
|
||||
};
|
||||
return tableRowMapper(node, columns, meta);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the given report node to table rows.
|
||||
* @param {IBalanceSheetDataNode} node -
|
||||
@@ -169,6 +195,10 @@ export default class BalanceSheetTable extends R.compose(
|
||||
this.isNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.ACCOUNT),
|
||||
this.accountNodeTableRowsMapper,
|
||||
],
|
||||
[
|
||||
this.isNodeType(BALANCE_SHEET_SCHEMA_NODE_TYPE.NET_INCOME),
|
||||
this.netIncomeNodeTableRowsMapper,
|
||||
],
|
||||
])(node);
|
||||
};
|
||||
|
||||
|
||||
@@ -9,5 +9,6 @@ export enum IROW_TYPE {
|
||||
AGGREGATE = 'AGGREGATE',
|
||||
ACCOUNTS = 'ACCOUNTS',
|
||||
ACCOUNT = 'ACCOUNT',
|
||||
NET_INCOME = 'NET_INCOME',
|
||||
TOTAL = 'TOTAL',
|
||||
}
|
||||
|
||||
@@ -10,13 +10,26 @@ import {
|
||||
} from '@/interfaces';
|
||||
import FinancialSheet from '../FinancialSheet';
|
||||
import { allPassedConditionsPass, flatToNestedArray } from 'utils';
|
||||
import { TrialBalanceSheetRepository } from './TrialBalanceSheetRepository';
|
||||
|
||||
export default class TrialBalanceSheet extends FinancialSheet {
|
||||
tenantId: number;
|
||||
query: ITrialBalanceSheetQuery;
|
||||
accounts: IAccount & { type: IAccountType }[];
|
||||
journalFinancial: any;
|
||||
baseCurrency: string;
|
||||
/**
|
||||
* Trial balance sheet query.
|
||||
* @param {ITrialBalanceSheetQuery} query
|
||||
*/
|
||||
private query: ITrialBalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Trial balance sheet repository.
|
||||
* @param {TrialBalanceSheetRepository}
|
||||
*/
|
||||
private repository: TrialBalanceSheetRepository;
|
||||
|
||||
/**
|
||||
* Organization base currency.
|
||||
* @param {string}
|
||||
*/
|
||||
private baseCurrency: string;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
@@ -28,20 +41,58 @@ export default class TrialBalanceSheet extends FinancialSheet {
|
||||
constructor(
|
||||
tenantId: number,
|
||||
query: ITrialBalanceSheetQuery,
|
||||
accounts: IAccount & { type: IAccountType }[],
|
||||
journalFinancial: any,
|
||||
repository: TrialBalanceSheetRepository,
|
||||
baseCurrency: string
|
||||
) {
|
||||
super();
|
||||
|
||||
this.tenantId = tenantId;
|
||||
this.query = query;
|
||||
this.accounts = accounts;
|
||||
this.journalFinancial = journalFinancial;
|
||||
this.repository = repository;
|
||||
this.numberFormat = this.query.numberFormat;
|
||||
this.baseCurrency = baseCurrency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the closing credit of the given account.
|
||||
* @param {number} accountId
|
||||
* @returns {number}
|
||||
*/
|
||||
public getClosingAccountCredit(accountId: number) {
|
||||
const depsAccountsIds =
|
||||
this.repository.accountsDepGraph.dependenciesOf(accountId);
|
||||
|
||||
return this.repository.totalAccountsLedger
|
||||
.whereAccountsIds([accountId, ...depsAccountsIds])
|
||||
.getClosingCredit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the closing debit of the given account.
|
||||
* @param {number} accountId
|
||||
* @returns {number}
|
||||
*/
|
||||
public getClosingAccountDebit(accountId: number) {
|
||||
const depsAccountsIds =
|
||||
this.repository.accountsDepGraph.dependenciesOf(accountId);
|
||||
|
||||
return this.repository.totalAccountsLedger
|
||||
.whereAccountsIds([accountId, ...depsAccountsIds])
|
||||
.getClosingDebit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the closing total of the given account.
|
||||
* @param {number} accountId
|
||||
* @returns {number}
|
||||
*/
|
||||
public getClosingAccountTotal(accountId: number) {
|
||||
const credit = this.getClosingAccountCredit(accountId);
|
||||
const debit = this.getClosingAccountDebit(accountId);
|
||||
|
||||
return debit - credit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Account mapper.
|
||||
* @param {IAccount} account
|
||||
@@ -50,23 +101,28 @@ export default class TrialBalanceSheet extends FinancialSheet {
|
||||
private accountTransformer = (
|
||||
account: IAccount & { type: IAccountType }
|
||||
): ITrialBalanceAccount => {
|
||||
const trial = this.journalFinancial.getTrialBalanceWithDepands(account.id);
|
||||
const debit = this.getClosingAccountDebit(account.id);
|
||||
const credit = this.getClosingAccountCredit(account.id);
|
||||
const balance = this.getClosingAccountTotal(account.id);
|
||||
|
||||
return {
|
||||
id: account.id,
|
||||
parentAccountId: account.parentAccountId,
|
||||
name: account.name,
|
||||
formattedName: account.code
|
||||
? `${account.name} - ${account.code}`
|
||||
: `${account.name}`,
|
||||
code: account.code,
|
||||
accountNormal: account.accountNormal,
|
||||
|
||||
credit: trial.credit,
|
||||
debit: trial.debit,
|
||||
balance: trial.balance,
|
||||
credit,
|
||||
debit,
|
||||
balance,
|
||||
currencyCode: this.baseCurrency,
|
||||
|
||||
formattedCredit: this.formatNumber(trial.credit),
|
||||
formattedDebit: this.formatNumber(trial.debit),
|
||||
formattedBalance: this.formatNumber(trial.balance),
|
||||
formattedCredit: this.formatNumber(credit),
|
||||
formattedDebit: this.formatNumber(debit),
|
||||
formattedBalance: this.formatNumber(balance),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -117,10 +173,7 @@ export default class TrialBalanceSheet extends FinancialSheet {
|
||||
private filterNoneTransactions = (
|
||||
accountNode: ITrialBalanceAccount
|
||||
): boolean => {
|
||||
const entries = this.journalFinancial.getAccountEntriesWithDepents(
|
||||
accountNode.id
|
||||
);
|
||||
return entries.length > 0;
|
||||
return false === this.repository.totalAccountsLedger.isEmpty();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -200,11 +253,11 @@ export default class TrialBalanceSheet extends FinancialSheet {
|
||||
*/
|
||||
public reportData(): ITrialBalanceSheetData {
|
||||
// Don't return noting if the journal has no transactions.
|
||||
if (this.journalFinancial.isEmpty()) {
|
||||
if (this.repository.totalAccountsLedger.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
// Retrieve accounts nodes.
|
||||
const accounts = this.accountsSection(this.accounts);
|
||||
const accounts = this.accountsSection(this.repository.accounts);
|
||||
|
||||
// Retrieve account node.
|
||||
const total = this.tatalSection(accounts);
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import { ITrialBalanceSheetQuery } from '@/interfaces';
|
||||
import Ledger from '@/services/Accounting/Ledger';
|
||||
import { Knex } from 'knex';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { Service } from 'typedi';
|
||||
|
||||
@Service()
|
||||
export class TrialBalanceSheetRepository {
|
||||
private query: ITrialBalanceSheetQuery;
|
||||
private models: any;
|
||||
public accounts: any;
|
||||
public accountsDepGraph;
|
||||
|
||||
/**
|
||||
* Total closing accounts ledger.
|
||||
* @param {Ledger}
|
||||
*/
|
||||
public totalAccountsLedger: Ledger;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {number} tenantId
|
||||
* @param {IBalanceSheetQuery} query
|
||||
*/
|
||||
constructor(models: any, repos: any, query: ITrialBalanceSheetQuery) {
|
||||
this.query = query;
|
||||
this.repos = repos;
|
||||
this.models = models;
|
||||
}
|
||||
|
||||
/**
|
||||
* Async initialize.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public asyncInitialize = async () => {
|
||||
await this.initAccounts();
|
||||
await this.initAccountsClosingTotalLedger();
|
||||
};
|
||||
|
||||
// ----------------------------
|
||||
// # Accounts
|
||||
// ----------------------------
|
||||
/**
|
||||
* Initialize accounts.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public initAccounts = async () => {
|
||||
const accounts = await this.getAccounts();
|
||||
const accountsDepGraph =
|
||||
await this.repos.accountRepository.getDependencyGraph();
|
||||
|
||||
this.accountsDepGraph = accountsDepGraph;
|
||||
this.accounts = accounts;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize all accounts closing total ledger.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public initAccountsClosingTotalLedger = async (): Promise<void> => {
|
||||
const totalByAccounts = await this.closingAccountsTotal(this.query.toDate);
|
||||
|
||||
this.totalAccountsLedger = Ledger.fromTransactions(totalByAccounts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve accounts of the report.
|
||||
* @return {Promise<IAccount[]>}
|
||||
*/
|
||||
private getAccounts = () => {
|
||||
const { Account } = this.models;
|
||||
|
||||
return Account.query();
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the opening balance transactions of the report.
|
||||
* @param {Date|string} openingDate -
|
||||
*/
|
||||
public closingAccountsTotal = async (openingDate: Date | string) => {
|
||||
const { AccountTransaction } = this.models;
|
||||
|
||||
return AccountTransaction.query().onBuild((query) => {
|
||||
query.sum('credit as credit');
|
||||
query.sum('debit as debit');
|
||||
query.groupBy('accountId');
|
||||
query.select(['accountId']);
|
||||
|
||||
query.modify('filterDateRange', null, openingDate);
|
||||
query.withGraphFetched('account');
|
||||
|
||||
this.commonFilterBranchesQuery(query);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Common branches filter query.
|
||||
* @param {Knex.QueryBuilder} query
|
||||
*/
|
||||
private commonFilterBranchesQuery = (query: Knex.QueryBuilder) => {
|
||||
if (!isEmpty(this.query.branchesIds)) {
|
||||
query.modify('filterByBranches', this.query.branchesIds);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -2,12 +2,18 @@ import { Service, Inject } from 'typedi';
|
||||
import moment from 'moment';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import Journal from '@/services/Accounting/JournalPoster';
|
||||
import { ITrialBalanceSheetMeta, ITrialBalanceSheetQuery, ITrialBalanceStatement } from '@/interfaces';
|
||||
import {
|
||||
ITrialBalanceSheetMeta,
|
||||
ITrialBalanceSheetQuery,
|
||||
ITrialBalanceStatement,
|
||||
} from '@/interfaces';
|
||||
import TrialBalanceSheet from './TrialBalanceSheet';
|
||||
import FinancialSheet from '../FinancialSheet';
|
||||
import InventoryService from '@/services/Inventory/Inventory';
|
||||
import { parseBoolean } from 'utils';
|
||||
import { Tenant } from '@/system/models';
|
||||
import { TrialBalanceSheetRepository } from './TrialBalanceSheetRepository';
|
||||
import { TrialBalanceSheetTable } from './TrialBalanceSheetTable';
|
||||
|
||||
@Service()
|
||||
export default class TrialBalanceSheetService extends FinancialSheet {
|
||||
@@ -51,9 +57,8 @@ export default class TrialBalanceSheetService extends FinancialSheet {
|
||||
reportMetadata(tenantId: number): ITrialBalanceSheetMeta {
|
||||
const settings = this.tenancy.settings(tenantId);
|
||||
|
||||
const isCostComputeRunning = this.inventoryService.isItemsCostComputeRunning(
|
||||
tenantId
|
||||
);
|
||||
const isCostComputeRunning =
|
||||
this.inventoryService.isItemsCostComputeRunning(tenantId);
|
||||
const organizationName = settings.get({
|
||||
group: 'organization',
|
||||
key: 'name',
|
||||
@@ -72,10 +77,8 @@ export default class TrialBalanceSheetService extends FinancialSheet {
|
||||
|
||||
/**
|
||||
* Retrieve trial balance sheet statement.
|
||||
* -------------
|
||||
* @param {number} tenantId
|
||||
* @param {IBalanceSheetQuery} query
|
||||
*
|
||||
* @return {IBalanceSheetStatement}
|
||||
*/
|
||||
public async trialBalanceSheet(
|
||||
@@ -86,43 +89,27 @@ export default class TrialBalanceSheetService extends FinancialSheet {
|
||||
...this.defaultQuery,
|
||||
...query,
|
||||
};
|
||||
const {
|
||||
accountRepository,
|
||||
transactionsRepository,
|
||||
} = this.tenancy.repositories(tenantId);
|
||||
|
||||
const tenant = await Tenant.query()
|
||||
.findById(tenantId)
|
||||
.withGraphFetched('metadata');
|
||||
|
||||
this.logger.info('[trial_balance_sheet] trying to calcualte the report.', {
|
||||
tenantId,
|
||||
filter,
|
||||
});
|
||||
// Retrieve all accounts on the storage.
|
||||
const accounts = await accountRepository.all();
|
||||
const accountsGraph = await accountRepository.getDependencyGraph();
|
||||
const models = this.tenancy.models(tenantId);
|
||||
const repos = this.tenancy.repositories(tenantId);
|
||||
|
||||
// Retrieve all journal transactions based on the given query.
|
||||
const transactions = await transactionsRepository.journal({
|
||||
fromDate: query.fromDate,
|
||||
toDate: query.toDate,
|
||||
sumationCreditDebit: true,
|
||||
branchesIds: query.branchesIds
|
||||
});
|
||||
// Transform transactions array to journal collection.
|
||||
const transactionsJournal = Journal.fromTransactions(
|
||||
transactions,
|
||||
tenantId,
|
||||
accountsGraph
|
||||
const trialBalanceSheetRepos = new TrialBalanceSheetRepository(
|
||||
models,
|
||||
repos,
|
||||
filter
|
||||
);
|
||||
await trialBalanceSheetRepos.asyncInitialize();
|
||||
|
||||
// Trial balance report instance.
|
||||
const trialBalanceInstance = new TrialBalanceSheet(
|
||||
tenantId,
|
||||
filter,
|
||||
accounts,
|
||||
transactionsJournal,
|
||||
tenant.metadata.baseCurrency,
|
||||
trialBalanceSheetRepos,
|
||||
tenant.metadata.baseCurrency
|
||||
);
|
||||
// Trial balance sheet data.
|
||||
const trialBalanceSheetData = trialBalanceInstance.reportData();
|
||||
@@ -133,4 +120,27 @@ export default class TrialBalanceSheetService extends FinancialSheet {
|
||||
meta: this.reportMetadata(tenantId),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the trial balance sheet table.
|
||||
* @param {number} tenantId
|
||||
* @param {ITrialBalanceSheetQuery} query
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
public async trialBalanceSheetTable(
|
||||
tenantId: number,
|
||||
query: ITrialBalanceSheetQuery
|
||||
) {
|
||||
const trialBalance = await this.trialBalanceSheet(tenantId, query);
|
||||
const table = new TrialBalanceSheetTable(trialBalance.data, query, {});
|
||||
|
||||
return {
|
||||
table: {
|
||||
columns: table.tableColumns(),
|
||||
rows: table.tableRows(),
|
||||
},
|
||||
meta: trialBalance.meta,
|
||||
query: trialBalance.query,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
import * as R from 'ramda';
|
||||
import FinancialSheet from '../FinancialSheet';
|
||||
import { FinancialTable } from '../FinancialTable';
|
||||
import {
|
||||
IBalanceSheetStatementData,
|
||||
ITableColumn,
|
||||
ITableColumnAccessor,
|
||||
ITableRow,
|
||||
ITrialBalanceAccount,
|
||||
ITrialBalanceSheetData,
|
||||
ITrialBalanceSheetQuery,
|
||||
ITrialBalanceTotal,
|
||||
} from '@/interfaces';
|
||||
import { tableRowMapper } from '@/utils';
|
||||
import { IROW_TYPE } from '../BalanceSheet/constants';
|
||||
import { FinancialSheetStructure } from '../FinancialSheetStructure';
|
||||
|
||||
export class TrialBalanceSheetTable extends R.compose(
|
||||
FinancialTable,
|
||||
FinancialSheetStructure
|
||||
)(FinancialSheet) {
|
||||
/**
|
||||
* @param {ITrialBalanceSheetData}
|
||||
*/
|
||||
public data: ITrialBalanceSheetData;
|
||||
|
||||
/**
|
||||
* Balance sheet query.
|
||||
* @param {ITrialBalanceSheetQuery}
|
||||
*/
|
||||
public query: ITrialBalanceSheetQuery;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {IBalanceSheetStatementData} reportData -
|
||||
* @param {ITrialBalanceSheetQuery} query -
|
||||
*/
|
||||
constructor(
|
||||
data: ITrialBalanceSheetData,
|
||||
query: ITrialBalanceSheetQuery,
|
||||
i18n: any
|
||||
) {
|
||||
super();
|
||||
|
||||
this.data = data;
|
||||
this.query = query;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the common columns for all report nodes.
|
||||
* @param {ITableColumnAccessor[]}
|
||||
*/
|
||||
private commonColumnsAccessors = (): ITableColumnAccessor[] => {
|
||||
return [
|
||||
{ key: 'account', accessor: 'formattedName' },
|
||||
{ key: 'debit', accessor: 'formattedDebit' },
|
||||
{ key: 'credit', accessor: 'formattedCredit' },
|
||||
{ key: 'total', accessor: 'formattedBalance' },
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the account node to table row.
|
||||
* @param {ITrialBalanceAccount} node -
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
private accountNodeTableRowsMapper = (
|
||||
node: ITrialBalanceAccount
|
||||
): ITableRow => {
|
||||
const columns = this.commonColumnsAccessors();
|
||||
const meta = {
|
||||
rowTypes: [IROW_TYPE.ACCOUNT],
|
||||
id: node.id,
|
||||
};
|
||||
return tableRowMapper(node, columns, meta);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the total node to table row.
|
||||
* @param {ITrialBalanceTotal} node -
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
private totalNodeTableRowsMapper = (node: ITrialBalanceTotal): ITableRow => {
|
||||
const columns = this.commonColumnsAccessors();
|
||||
const meta = {
|
||||
rowTypes: [IROW_TYPE.TOTAL],
|
||||
id: node.id,
|
||||
};
|
||||
return tableRowMapper(node, columns, meta);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mappes the given report sections to table rows.
|
||||
* @param {IBalanceSheetDataNode[]} nodes -
|
||||
* @return {ITableRow}
|
||||
*/
|
||||
private accountsToTableRowsMap = (
|
||||
nodes: ITrialBalanceAccount[]
|
||||
): ITableRow[] => {
|
||||
return this.mapNodesDeep(nodes, this.accountNodeTableRowsMapper);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the accounts table rows of the given report data.
|
||||
* @returns {ITableRow[]}
|
||||
*/
|
||||
private accountsTableRows = (): ITableRow[] => {
|
||||
return this.accountsToTableRowsMap(this.data.accounts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps the given total node to table row.
|
||||
* @returns {ITableRow}
|
||||
*/
|
||||
private totalTableRow = (): ITableRow => {
|
||||
return this.totalNodeTableRowsMapper(this.data.total);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the table rows.
|
||||
* @returns {ITableRow[]}
|
||||
*/
|
||||
public tableRows = (): ITableRow[] => {
|
||||
return R.compose(
|
||||
R.append(this.totalTableRow()),
|
||||
R.concat(this.accountsTableRows())
|
||||
)([]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrrieves the table columns.
|
||||
* @returns {ITableColumn[]}
|
||||
*/
|
||||
public tableColumns = (): ITableColumn[] => {
|
||||
return R.compose(
|
||||
this.tableColumnsCellIndexing,
|
||||
R.concat([
|
||||
{ key: 'account_name', label: 'Account' },
|
||||
{ key: 'debit', label: 'Debit' },
|
||||
{ key: 'credit', label: 'Credit' },
|
||||
{ key: 'total', label: 'Total' },
|
||||
])
|
||||
)([]);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
export enum IROW_TYPE {
|
||||
ACCOUNT = 'ACCOUNT',
|
||||
TOTAL = 'TOTAL',
|
||||
}
|
||||
@@ -55,6 +55,18 @@ export class CreateItem {
|
||||
itemDTO.inventoryAccountId
|
||||
);
|
||||
}
|
||||
if (itemDTO.purchaseTaxRateId) {
|
||||
await this.validators.validatePurchaseTaxRateExistance(
|
||||
tenantId,
|
||||
itemDTO.purchaseTaxRateId
|
||||
);
|
||||
}
|
||||
if (itemDTO.sellTaxRateId) {
|
||||
await this.validators.validateSellTaxRateExistance(
|
||||
tenantId,
|
||||
itemDTO.sellTaxRateId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -76,6 +76,20 @@ export class EditItem {
|
||||
itemDTO.inventoryAccountId
|
||||
);
|
||||
}
|
||||
// Validate the purchase tax rate id existance.
|
||||
if (itemDTO.purchaseTaxRateId) {
|
||||
await this.validators.validatePurchaseTaxRateExistance(
|
||||
tenantId,
|
||||
itemDTO.purchaseTaxRateId
|
||||
);
|
||||
}
|
||||
// Validate the sell tax rate id existance.
|
||||
if (itemDTO.sellTaxRateId) {
|
||||
await this.validators.validateSellTaxRateExistance(
|
||||
tenantId,
|
||||
itemDTO.sellTaxRateId
|
||||
);
|
||||
}
|
||||
// Validate inventory account should be modified in inventory item
|
||||
// has inventory transactions.
|
||||
await this.validators.validateItemInvnetoryAccountModified(
|
||||
|
||||
@@ -27,6 +27,8 @@ export class GetItem {
|
||||
.withGraphFetched('category')
|
||||
.withGraphFetched('costAccount')
|
||||
.withGraphFetched('itemWarehouses.warehouse')
|
||||
.withGraphFetched('sellTaxRate')
|
||||
.withGraphFetched('purchaseTaxRate')
|
||||
.throwIfNotFound();
|
||||
|
||||
return this.transformer.transform(tenantId, item, new ItemTransformer());
|
||||
|
||||
@@ -241,4 +241,40 @@ export class ItemsValidators {
|
||||
throw new ServiceError(ERRORS.ITEM_CANNOT_CHANGE_INVENTORY_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the purchase tax rate id existance.
|
||||
* @param {number} tenantId -
|
||||
* @param {number} taxRateId -
|
||||
*/
|
||||
public async validatePurchaseTaxRateExistance(
|
||||
tenantId: number,
|
||||
taxRateId: number
|
||||
) {
|
||||
const { TaxRate } = this.tenancy.models(tenantId);
|
||||
|
||||
const foundTaxRate = await TaxRate.query().findById(taxRateId);
|
||||
|
||||
if (!foundTaxRate) {
|
||||
throw new ServiceError(ERRORS.PURCHASE_TAX_RATE_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the sell tax rate id existance.
|
||||
* @param {number} tenantId
|
||||
* @param {number} taxRateId
|
||||
*/
|
||||
public async validateSellTaxRateExistance(
|
||||
tenantId: number,
|
||||
taxRateId: number
|
||||
) {
|
||||
const { TaxRate } = this.tenancy.models(tenantId);
|
||||
|
||||
const foundTaxRate = await TaxRate.query().findById(taxRateId);
|
||||
|
||||
if (!foundTaxRate) {
|
||||
throw new ServiceError(ERRORS.SELL_TAX_RATE_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,10 @@ export const ERRORS = {
|
||||
TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS: 'TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS',
|
||||
INVENTORY_ACCOUNT_CANNOT_MODIFIED: 'INVENTORY_ACCOUNT_CANNOT_MODIFIED',
|
||||
|
||||
ITEM_HAS_ASSOCIATED_TRANSACTIONS: 'ITEM_HAS_ASSOCIATED_TRANSACTIONS'
|
||||
ITEM_HAS_ASSOCIATED_TRANSACTIONS: 'ITEM_HAS_ASSOCIATED_TRANSACTIONS',
|
||||
|
||||
PURCHASE_TAX_RATE_NOT_FOUND: 'PURCHASE_TAX_RATE_NOT_FOUND',
|
||||
SELL_TAX_RATE_NOT_FOUND: 'SELL_TAX_RATE_NOT_FOUND',
|
||||
};
|
||||
|
||||
export const DEFAULT_VIEW_COLUMNS = [];
|
||||
|
||||
@@ -203,8 +203,8 @@ export class BillPaymentGLEntries {
|
||||
|
||||
/**
|
||||
* Retrieves the payment GL payable entry.
|
||||
* @param {IBillPayment} billPayment
|
||||
* @param {number} APAccountId
|
||||
* @param {IBillPayment} billPayment
|
||||
* @param {number} APAccountId
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getPaymentGLPayableEntry = (
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
import { BranchTransactionDTOTransform } from '@/services/Branches/Integrations/BranchTransactionDTOTransform';
|
||||
import { WarehouseTransactionDTOTransform } from '@/services/Warehouses/Integrations/WarehouseTransactionDTOTransform';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { ItemEntriesTaxTransactions } from '@/services/TaxRates/ItemEntriesTaxTransactions';
|
||||
|
||||
@Service()
|
||||
export class BillDTOTransformer {
|
||||
@@ -23,6 +24,9 @@ export class BillDTOTransformer {
|
||||
@Inject()
|
||||
private warehouseDTOTransform: WarehouseTransactionDTOTransform;
|
||||
|
||||
@Inject()
|
||||
private taxDTOTransformer: ItemEntriesTaxTransactions;
|
||||
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@@ -73,14 +77,24 @@ export class BillDTOTransformer {
|
||||
const billNumber = billDTO.billNumber || oldBill?.billNumber;
|
||||
|
||||
const initialEntries = billDTO.entries.map((entry) => ({
|
||||
reference_type: 'Bill',
|
||||
referenceType: 'Bill',
|
||||
isInclusiveTax: billDTO.isInclusiveTax,
|
||||
...omit(entry, ['amount']),
|
||||
}));
|
||||
const entries = await composeAsync(
|
||||
const asyncEntries = await composeAsync(
|
||||
// Associate tax rate from tax id to entries.
|
||||
this.taxDTOTransformer.assocTaxRateFromTaxIdToEntries(tenantId),
|
||||
// Associate tax rate id from tax code to entries.
|
||||
this.taxDTOTransformer.assocTaxRateIdFromCodeToEntries(tenantId),
|
||||
// Sets the default cost account to the bill entries.
|
||||
this.setBillEntriesDefaultAccounts(tenantId)
|
||||
)(initialEntries);
|
||||
|
||||
const entries = R.compose(
|
||||
// Remove tax code from entries.
|
||||
R.map(R.omit(['taxCode']))
|
||||
)(asyncEntries);
|
||||
|
||||
const initialDTO = {
|
||||
...formatDateFields(omit(billDTO, ['open', 'entries']), [
|
||||
'billDate',
|
||||
@@ -100,6 +114,8 @@ export class BillDTOTransformer {
|
||||
userId: authorizedUser.id,
|
||||
};
|
||||
return R.compose(
|
||||
// Associates tax amount withheld to the model.
|
||||
this.taxDTOTransformer.assocTaxAmountWithheldFromEntries,
|
||||
this.branchDTOTransform.transformDTO(tenantId),
|
||||
this.warehouseDTOTransform.transformDTO(tenantId)
|
||||
)(initialDTO);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { AccountNormal, IBill, IItemEntry, ILedgerEntry } from '@/interfaces';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import Ledger from '@/services/Accounting/Ledger';
|
||||
import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
|
||||
import ItemsEntriesService from '@/services/Items/ItemsEntriesService';
|
||||
|
||||
@Service()
|
||||
export class BillGLEntries {
|
||||
@@ -16,6 +17,9 @@ export class BillGLEntries {
|
||||
@Inject()
|
||||
private ledgerStorage: LedgerStorageService;
|
||||
|
||||
@Inject()
|
||||
private itemsEntriesService: ItemsEntriesService;
|
||||
|
||||
/**
|
||||
* Creates bill GL entries.
|
||||
* @param {number} tenantId -
|
||||
@@ -43,8 +47,16 @@ export class BillGLEntries {
|
||||
{},
|
||||
trx
|
||||
);
|
||||
const billLedger = this.getBillLedger(bill, APAccount.id);
|
||||
|
||||
// Find or create tax payable account.
|
||||
const taxPayableAccount = await accountRepository.findOrCreateTaxPayable(
|
||||
{},
|
||||
trx
|
||||
);
|
||||
const billLedger = this.getBillLedger(
|
||||
bill,
|
||||
APAccount.id,
|
||||
taxPayableAccount.id
|
||||
);
|
||||
// Commit the GL enties on the storage.
|
||||
await this.ledgerStorage.commit(tenantId, billLedger, trx);
|
||||
};
|
||||
@@ -83,7 +95,7 @@ export class BillGLEntries {
|
||||
|
||||
/**
|
||||
* Retrieves the bill common entry.
|
||||
* @param {IBill} bill
|
||||
* @param {IBill} bill
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getBillCommonEntry = (bill: IBill) => {
|
||||
@@ -119,7 +131,7 @@ export class BillGLEntries {
|
||||
(bill: IBill, entry: IItemEntry, index: number): ILedgerEntry => {
|
||||
const commonJournalMeta = this.getBillCommonEntry(bill);
|
||||
|
||||
const localAmount = bill.exchangeRate * entry.amount;
|
||||
const localAmount = bill.exchangeRate * entry.amountExludingTax;
|
||||
const landedCostAmount = sumBy(entry.allocatedCostEntries, 'cost');
|
||||
|
||||
return {
|
||||
@@ -173,7 +185,7 @@ export class BillGLEntries {
|
||||
|
||||
return {
|
||||
...commonJournalMeta,
|
||||
credit: bill.localAmount,
|
||||
credit: bill.totalLocal,
|
||||
accountId: payableAccountId,
|
||||
contactId: bill.vendorId,
|
||||
accountNormal: AccountNormal.CREDIT,
|
||||
@@ -182,15 +194,62 @@ export class BillGLEntries {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the bill tax GL entry.
|
||||
* @param {IBill} bill -
|
||||
* @param {number} taxPayableAccountId -
|
||||
* @param {IItemEntry} entry -
|
||||
* @param {number} index -
|
||||
* @returns {ILedgerEntry}
|
||||
*/
|
||||
private getBillTaxEntry = R.curry(
|
||||
(
|
||||
bill: IBill,
|
||||
taxPayableAccountId: number,
|
||||
entry: IItemEntry,
|
||||
index: number
|
||||
): ILedgerEntry => {
|
||||
const commonJournalMeta = this.getBillCommonEntry(bill);
|
||||
|
||||
return {
|
||||
...commonJournalMeta,
|
||||
debit: entry.taxAmount,
|
||||
index,
|
||||
indexGroup: 30,
|
||||
accountId: taxPayableAccountId,
|
||||
accountNormal: AccountNormal.CREDIT,
|
||||
taxRateId: entry.taxRateId,
|
||||
taxRate: entry.taxRate,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieves the bill tax GL entries.
|
||||
* @param {IBill} bill
|
||||
* @param {number} taxPayableAccountId
|
||||
* @returns {ILedgerEntry[]}
|
||||
*/
|
||||
private getBillTaxEntries = (bill: IBill, taxPayableAccountId: number) => {
|
||||
// Retrieves the non-zero tax entries.
|
||||
const nonZeroTaxEntries = this.itemsEntriesService.getNonZeroEntries(
|
||||
bill.entries
|
||||
);
|
||||
const transformTaxEntry = this.getBillTaxEntry(bill, taxPayableAccountId);
|
||||
|
||||
return nonZeroTaxEntries.map(transformTaxEntry);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the given bill GL entries.
|
||||
* @param {IBill} bill
|
||||
* @param {number} payableAccountId
|
||||
* @param {IBill} bill
|
||||
* @param {number} payableAccountId
|
||||
* @returns {ILedgerEntry[]}
|
||||
*/
|
||||
private getBillGLEntries = (
|
||||
bill: IBill,
|
||||
payableAccountId: number
|
||||
payableAccountId: number,
|
||||
taxPayableAccountId: number
|
||||
): ILedgerEntry[] => {
|
||||
const payableEntry = this.getBillPayableEntry(payableAccountId, bill);
|
||||
|
||||
@@ -201,18 +260,28 @@ export class BillGLEntries {
|
||||
const landedCostEntries = bill.locatedLandedCosts.map(
|
||||
landedCostTransformer
|
||||
);
|
||||
const taxEntries = this.getBillTaxEntries(bill, taxPayableAccountId);
|
||||
|
||||
// Allocate cost entries journal entries.
|
||||
return [payableEntry, ...itemsEntries, ...landedCostEntries];
|
||||
return [payableEntry, ...itemsEntries, ...landedCostEntries, ...taxEntries];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the given bill ledger.
|
||||
* @param {IBill} bill
|
||||
* @param {number} payableAccountId
|
||||
* @param {IBill} bill
|
||||
* @param {number} payableAccountId
|
||||
* @returns {Ledger}
|
||||
*/
|
||||
private getBillLedger = (bill: IBill, payableAccountId: number) => {
|
||||
const entries = this.getBillGLEntries(bill, payableAccountId);
|
||||
private getBillLedger = (
|
||||
bill: IBill,
|
||||
payableAccountId: number,
|
||||
taxPayableAccountId: number
|
||||
) => {
|
||||
const entries = this.getBillGLEntries(
|
||||
bill,
|
||||
payableAccountId,
|
||||
taxPayableAccountId
|
||||
);
|
||||
|
||||
return new Ledger(entries);
|
||||
};
|
||||
|
||||
@@ -28,7 +28,8 @@ export class GetBill {
|
||||
.findById(billId)
|
||||
.withGraphFetched('vendor')
|
||||
.withGraphFetched('entries.item')
|
||||
.withGraphFetched('branch');
|
||||
.withGraphFetched('branch')
|
||||
.withGraphFetched('taxes.taxRate');
|
||||
|
||||
// Validates the bill existance.
|
||||
this.validators.validateBillExistance(bill);
|
||||
|
||||
@@ -1,27 +1,42 @@
|
||||
import { IBill } from '@/interfaces';
|
||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||
import { SaleInvoiceTaxEntryTransformer } from '@/services/Sales/Invoices/SaleInvoiceTaxEntryTransformer';
|
||||
import { formatNumber } from 'utils';
|
||||
|
||||
export class PurchaseInvoiceTransformer extends Transformer {
|
||||
/**
|
||||
* Include these attributes to sale invoice object.
|
||||
* Include these attributes to sale bill object.
|
||||
* @returns {Array}
|
||||
*/
|
||||
public includeAttributes = (): string[] => {
|
||||
return [
|
||||
'formattedBillDate',
|
||||
'formattedDueDate',
|
||||
'formattedAmount',
|
||||
'formattedPaymentAmount',
|
||||
'formattedBalance',
|
||||
'formattedDueAmount',
|
||||
'formattedExchangeRate',
|
||||
'subtotalFormatted',
|
||||
'subtotalLocalFormatted',
|
||||
'subtotalExcludingTaxFormatted',
|
||||
'taxAmountWithheldLocalFormatted',
|
||||
'totalFormatted',
|
||||
'totalLocalFormatted',
|
||||
'taxes',
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted invoice date.
|
||||
* @param {IBill} invoice
|
||||
* Excluded attributes.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
public excludeAttributes = (): string[] => {
|
||||
return ['amount', 'amountLocal', 'localAmount'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted bill date.
|
||||
* @param {IBill} bill
|
||||
* @returns {String}
|
||||
*/
|
||||
protected formattedBillDate = (bill: IBill): string => {
|
||||
@@ -29,8 +44,8 @@ export class PurchaseInvoiceTransformer extends Transformer {
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve formatted invoice date.
|
||||
* @param {IBill} invoice
|
||||
* Retrieve formatted bill date.
|
||||
* @param {IBill} bill
|
||||
* @returns {String}
|
||||
*/
|
||||
protected formattedDueDate = (bill: IBill): string => {
|
||||
@@ -39,7 +54,7 @@ export class PurchaseInvoiceTransformer extends Transformer {
|
||||
|
||||
/**
|
||||
* Retrieve formatted bill amount.
|
||||
* @param {IBill} invoice
|
||||
* @param {IBill} bill
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedAmount = (bill): string => {
|
||||
@@ -48,7 +63,7 @@ export class PurchaseInvoiceTransformer extends Transformer {
|
||||
|
||||
/**
|
||||
* Retrieve formatted bill amount.
|
||||
* @param {IBill} invoice
|
||||
* @param {IBill} bill
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedPaymentAmount = (bill): string => {
|
||||
@@ -59,7 +74,7 @@ export class PurchaseInvoiceTransformer extends Transformer {
|
||||
|
||||
/**
|
||||
* Retrieve formatted bill amount.
|
||||
* @param {IBill} invoice
|
||||
* @param {IBill} bill
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedDueAmount = (bill): string => {
|
||||
@@ -77,10 +92,90 @@ export class PurchaseInvoiceTransformer extends Transformer {
|
||||
|
||||
/**
|
||||
* Retrieve the formatted exchange rate.
|
||||
* @param {ISaleInvoice} invoice
|
||||
* @param {IBill} bill
|
||||
* @returns {string}
|
||||
*/
|
||||
protected formattedExchangeRate = (invoice): string => {
|
||||
return formatNumber(invoice.exchangeRate, { money: false });
|
||||
protected formattedExchangeRate = (bill): string => {
|
||||
return formatNumber(bill.exchangeRate, {
|
||||
currencyCode: this.context.organization.baseCurrency,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the formatted subtotal.
|
||||
* @param {IBill} bill
|
||||
* @returns {string}
|
||||
*/
|
||||
protected subtotalFormatted = (bill): string => {
|
||||
return formatNumber(bill.subtotal, {
|
||||
currencyCode: bill.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the local subtotal formatted.
|
||||
* @param {IBill} bill
|
||||
* @returns {string}
|
||||
*/
|
||||
protected subtotalLocalFormatted = (bill): string => {
|
||||
return formatNumber(bill.subtotalLocal, {
|
||||
currencyCode: this.context.organization.baseCurrency,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the formatted subtotal tax excluded.
|
||||
* @param {IBill} bill
|
||||
* @returns {string}
|
||||
*/
|
||||
protected subtotalExcludingTaxFormatted = (bill): string => {
|
||||
return formatNumber(bill.subtotalExludingTax, {
|
||||
currencyCode: bill.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the local formatted tax amount withheld
|
||||
* @param {IBill} bill
|
||||
* @returns {string}
|
||||
*/
|
||||
protected taxAmountWithheldLocalFormatted = (bill): string => {
|
||||
return formatNumber(bill.taxAmountWithheldLocal, {
|
||||
currencyCode: this.context.organization.baseCurrency,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the total formatted.
|
||||
* @param {IBill} bill
|
||||
* @returns {string}
|
||||
*/
|
||||
protected totalFormatted = (bill): string => {
|
||||
return formatNumber(bill.total, {
|
||||
currencyCode: bill.currencyCode,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the local total formatted.
|
||||
* @param {IBill} bill
|
||||
* @returns {string}
|
||||
*/
|
||||
protected totalLocalFormatted = (bill): string => {
|
||||
return formatNumber(bill.totalLocal, {
|
||||
currencyCode: this.context.organization.baseCurrency,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the taxes lines of bill.
|
||||
* @param {Bill} bill
|
||||
*/
|
||||
protected taxes = (bill) => {
|
||||
return this.item(bill.taxes, new SaleInvoiceTaxEntryTransformer(), {
|
||||
subtotal: bill.subtotal,
|
||||
isInclusiveTax: bill.isInclusiveTax,
|
||||
currencyCode: bill.currencyCode,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -218,7 +218,8 @@ export class SaleInvoiceGLEntries {
|
||||
...commonEntry,
|
||||
credit: entry.taxAmount,
|
||||
accountId: taxPayableAccountId,
|
||||
index: index + 3,
|
||||
index: index + 1,
|
||||
indexGroup: 30,
|
||||
accountNormal: AccountNormal.CREDIT,
|
||||
taxRateId: entry.taxRateId,
|
||||
taxRate: entry.taxRate,
|
||||
|
||||
@@ -62,8 +62,8 @@ export class SaleInvoiceTaxEntryTransformer extends Transformer {
|
||||
const taxRate = this.taxRate(taxEntry);
|
||||
|
||||
return this.options.isInclusiveTax
|
||||
? getInclusiveTaxAmount(this.options.amount, taxRate)
|
||||
: getExlusiveTaxAmount(this.options.amount, taxRate);
|
||||
? getInclusiveTaxAmount(this.options.subtotal, taxRate)
|
||||
: getExlusiveTaxAmount(this.options.subtotal, taxRate);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -171,7 +171,7 @@ export class SaleInvoiceTransformer extends Transformer {
|
||||
*/
|
||||
protected taxes = (invoice) => {
|
||||
return this.item(invoice.taxes, new SaleInvoiceTaxEntryTransformer(), {
|
||||
amount: invoice.amount,
|
||||
subtotal: invoice.subtotal,
|
||||
isInclusiveTax: invoice.isInclusiveTax,
|
||||
currencyCode: invoice.currencyCode,
|
||||
});
|
||||
|
||||
@@ -115,6 +115,7 @@ export class EditTaxRateService {
|
||||
// Triggers `onTaxRateEdited` event.
|
||||
await this.eventPublisher.emitAsync(events.taxRates.onEdited, {
|
||||
editTaxRateDTO,
|
||||
oldTaxRate,
|
||||
taxRate,
|
||||
tenantId,
|
||||
trx,
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import HasTenancyService from '../Tenancy/TenancyService';
|
||||
|
||||
@Service()
|
||||
export class SyncItemTaxRateOnEditTaxRate {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
/**
|
||||
* Syncs the new tax rate created to item default sell tax rate.
|
||||
* @param {number} tenantId
|
||||
* @param {number} itemId
|
||||
* @param {number} sellTaxRateId
|
||||
*/
|
||||
public updateItemSellTaxRate = async (
|
||||
tenantId: number,
|
||||
oldSellTaxRateId: number,
|
||||
sellTaxRateId: number,
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
const { Item } = this.tenancy.models(tenantId);
|
||||
|
||||
// Can't continue if the old and new sell tax rate id are equal.
|
||||
if (oldSellTaxRateId === sellTaxRateId) return;
|
||||
|
||||
await Item.query().where('sellTaxRateId', oldSellTaxRateId).update({
|
||||
sellTaxRateId,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Syncs the new tax rate created to item default purchase tax rate.
|
||||
* @param {number} tenantId
|
||||
* @param {number} itemId
|
||||
* @param {number} purchaseTaxRateId
|
||||
*/
|
||||
public updateItemPurchaseTaxRate = async (
|
||||
tenantId: number,
|
||||
oldPurchaseTaxRateId: number,
|
||||
purchaseTaxRateId: number,
|
||||
trx?: Knex.Transaction
|
||||
) => {
|
||||
const { Item } = this.tenancy.models(tenantId);
|
||||
|
||||
// Can't continue if the old and new sell tax rate id are equal.
|
||||
if (oldPurchaseTaxRateId === purchaseTaxRateId) return;
|
||||
|
||||
await Item.query(trx)
|
||||
.where('purchaseTaxRateId', oldPurchaseTaxRateId)
|
||||
.update({
|
||||
purchaseTaxRateId,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { SyncItemTaxRateOnEditTaxRate } from './SyncItemTaxRateOnEditTaxRate';
|
||||
import events from '@/subscribers/events';
|
||||
import { ITaxRateEditedPayload } from '@/interfaces';
|
||||
import { runAfterTransaction } from '../UnitOfWork/TransactionsHooks';
|
||||
|
||||
@Service()
|
||||
export class SyncItemTaxRateOnEditTaxSubscriber {
|
||||
@Inject()
|
||||
private syncItemRateOnEdit: SyncItemTaxRateOnEditTaxRate;
|
||||
|
||||
/**
|
||||
* Attaches events with handles.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.taxRates.onEdited,
|
||||
this.handleSyncNewTaxRateToItemTaxRate
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncs the new tax rate created to default item tax rates.
|
||||
* @param {ITaxRateEditedPayload} payload -
|
||||
*/
|
||||
private handleSyncNewTaxRateToItemTaxRate = async ({
|
||||
taxRate,
|
||||
tenantId,
|
||||
oldTaxRate,
|
||||
trx,
|
||||
}: ITaxRateEditedPayload) => {
|
||||
runAfterTransaction(trx, async () => {
|
||||
await this.syncItemRateOnEdit.updateItemPurchaseTaxRate(
|
||||
tenantId,
|
||||
oldTaxRate.id,
|
||||
taxRate.id
|
||||
);
|
||||
await this.syncItemRateOnEdit.updateItemSellTaxRate(
|
||||
tenantId,
|
||||
oldTaxRate.id,
|
||||
taxRate.id
|
||||
);
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { IBillCreatingPayload, IBillEditingPayload } from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { CommandTaxRatesValidators } from '../CommandTaxRatesValidators';
|
||||
|
||||
@Service()
|
||||
export class BillTaxRateValidateSubscriber {
|
||||
@Inject()
|
||||
private taxRateDTOValidator: CommandTaxRatesValidators;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.bill.onCreating,
|
||||
this.validateBillEntriesTaxCodeExistanceOnCreating
|
||||
);
|
||||
bus.subscribe(
|
||||
events.bill.onCreating,
|
||||
this.validateBillEntriesTaxIdExistanceOnCreating
|
||||
);
|
||||
bus.subscribe(
|
||||
events.bill.onEditing,
|
||||
this.validateBillEntriesTaxCodeExistanceOnEditing
|
||||
);
|
||||
bus.subscribe(
|
||||
events.bill.onEditing,
|
||||
this.validateBillEntriesTaxIdExistanceOnEditing
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate bill entries tax rate code existance when creating.
|
||||
* @param {IBillCreatingPayload}
|
||||
*/
|
||||
private validateBillEntriesTaxCodeExistanceOnCreating = async ({
|
||||
billDTO,
|
||||
tenantId,
|
||||
}: IBillCreatingPayload) => {
|
||||
await this.taxRateDTOValidator.validateItemEntriesTaxCode(
|
||||
tenantId,
|
||||
billDTO.entries
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate the tax rate id existance when creating.
|
||||
* @param {IBillCreatingPayload}
|
||||
*/
|
||||
private validateBillEntriesTaxIdExistanceOnCreating = async ({
|
||||
billDTO,
|
||||
tenantId,
|
||||
}: IBillCreatingPayload) => {
|
||||
await this.taxRateDTOValidator.validateItemEntriesTaxCodeId(
|
||||
tenantId,
|
||||
billDTO.entries
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate bill entries tax rate code existance when editing.
|
||||
* @param {IBillEditingPayload}
|
||||
*/
|
||||
private validateBillEntriesTaxCodeExistanceOnEditing = async ({
|
||||
tenantId,
|
||||
billDTO,
|
||||
}: IBillEditingPayload) => {
|
||||
await this.taxRateDTOValidator.validateItemEntriesTaxCode(
|
||||
tenantId,
|
||||
billDTO.entries
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates the bill entries tax rate id existance when editing.
|
||||
* @param {ISaleInvoiceEditingPayload} payload -
|
||||
*/
|
||||
private validateBillEntriesTaxIdExistanceOnEditing = async ({
|
||||
tenantId,
|
||||
billDTO,
|
||||
}: IBillEditingPayload) => {
|
||||
await this.taxRateDTOValidator.validateItemEntriesTaxCodeId(
|
||||
tenantId,
|
||||
billDTO.entries
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import {
|
||||
IBIllEventDeletedPayload,
|
||||
IBillCreatedPayload,
|
||||
IBillEditedPayload,
|
||||
ISaleInvoiceCreatedPayload,
|
||||
ISaleInvoiceDeletedPayload,
|
||||
ISaleInvoiceEditedPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { WriteTaxTransactionsItemEntries } from '../WriteTaxTransactionsItemEntries';
|
||||
|
||||
@Service()
|
||||
export class WriteBillTaxTransactionsSubscriber {
|
||||
@Inject()
|
||||
private writeTaxTransactions: WriteTaxTransactionsItemEntries;
|
||||
|
||||
/**
|
||||
* Attaches events with handlers.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.bill.onCreated,
|
||||
this.writeInvoiceTaxTransactionsOnCreated
|
||||
);
|
||||
bus.subscribe(
|
||||
events.bill.onEdited,
|
||||
this.rewriteInvoiceTaxTransactionsOnEdited
|
||||
);
|
||||
bus.subscribe(
|
||||
events.bill.onDeleted,
|
||||
this.removeInvoiceTaxTransactionsOnDeleted
|
||||
);
|
||||
return bus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the bill tax transactions on invoice created.
|
||||
* @param {ISaleInvoiceCreatingPaylaod}
|
||||
*/
|
||||
private writeInvoiceTaxTransactionsOnCreated = async ({
|
||||
tenantId,
|
||||
bill,
|
||||
trx,
|
||||
}: IBillCreatedPayload) => {
|
||||
await this.writeTaxTransactions.writeTaxTransactionsFromItemEntries(
|
||||
tenantId,
|
||||
bill.entries,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Rewrites the bill tax transactions on invoice edited.
|
||||
* @param {IBillEditedPayload} payload -
|
||||
*/
|
||||
private rewriteInvoiceTaxTransactionsOnEdited = async ({
|
||||
tenantId,
|
||||
bill,
|
||||
trx,
|
||||
}: IBillEditedPayload) => {
|
||||
await this.writeTaxTransactions.rewriteTaxRateTransactionsFromItemEntries(
|
||||
tenantId,
|
||||
bill.entries,
|
||||
'Bill',
|
||||
bill.id,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the invoice tax transactions on invoice deleted.
|
||||
* @param {IBIllEventDeletedPayload}
|
||||
*/
|
||||
private removeInvoiceTaxTransactionsOnDeleted = async ({
|
||||
tenantId,
|
||||
oldBill,
|
||||
trx,
|
||||
}: IBIllEventDeletedPayload) => {
|
||||
await this.writeTaxTransactions.removeTaxTransactionsFromItemEntries(
|
||||
tenantId,
|
||||
oldBill.id,
|
||||
'Bill',
|
||||
trx
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import events from '@/subscribers/events';
|
||||
import TenancyService from '@/services/Tenancy/TenancyService';
|
||||
import BillsService from '@/services/Purchases/Bills';
|
||||
import {
|
||||
IBillCreatedPayload,
|
||||
IBillEditedPayload,
|
||||
IBIllEventDeletedPayload,
|
||||
} from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
export default class BillWriteGLEntriesSubscriber {
|
||||
@Inject()
|
||||
tenancy: TenancyService;
|
||||
|
||||
@Inject()
|
||||
billsService: BillsService;
|
||||
|
||||
/**
|
||||
* Attaches events with handles.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.bill.onCreated,
|
||||
this.handlerWriteJournalEntriesOnCreate
|
||||
);
|
||||
bus.subscribe(
|
||||
events.bill.onEdited,
|
||||
this.handleOverwriteJournalEntriesOnEdit
|
||||
);
|
||||
bus.subscribe(events.bill.onDeleted, this.handlerDeleteJournalEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles writing journal entries once bill created.
|
||||
* @param {IBillCreatedPayload} payload -
|
||||
*/
|
||||
private handlerWriteJournalEntriesOnCreate = async ({
|
||||
tenantId,
|
||||
billId,
|
||||
bill,
|
||||
trx,
|
||||
}: IBillCreatedPayload) => {
|
||||
// Can't continue if the bill is not opened yet.
|
||||
if (!bill.openedAt) return null;
|
||||
|
||||
await this.billsService.recordJournalTransactions(
|
||||
tenantId,
|
||||
billId,
|
||||
false,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the overwriting journal entries once bill edited.
|
||||
* @param {IBillEditedPayload} payload -
|
||||
*/
|
||||
private handleOverwriteJournalEntriesOnEdit = async ({
|
||||
tenantId,
|
||||
billId,
|
||||
bill,
|
||||
trx,
|
||||
}: IBillEditedPayload) => {
|
||||
// Can't continue if the bill is not opened yet.
|
||||
if (!bill.openedAt) return null;
|
||||
|
||||
await this.billsService.recordJournalTransactions(
|
||||
tenantId,
|
||||
billId,
|
||||
true,
|
||||
trx
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles revert journal entries on bill deleted.
|
||||
* @param {IBIllEventDeletedPayload} payload -
|
||||
*/
|
||||
private handlerDeleteJournalEntries = async ({
|
||||
tenantId,
|
||||
billId,
|
||||
trx,
|
||||
}: IBIllEventDeletedPayload) => {
|
||||
await this.billsService.revertJournalEntries(tenantId, billId, trx);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
exports.up = function (knex) {
|
||||
return knex.schema.table('tenants_metadata', (table) => {
|
||||
table.string('tax_number')
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function (knex) {
|
||||
return knex.schema.table('tenants_metadata', (table) => {
|
||||
table.dropColumn('tax_number');
|
||||
});
|
||||
};
|
||||
@@ -41,10 +41,10 @@ const mapValuesDeepReverse = (nodes, callback, config?) => {
|
||||
);
|
||||
const mappedNode = callback(node, children);
|
||||
|
||||
_.set(clonedNodes, pathString, {
|
||||
...mappedNode,
|
||||
...(!_.isEmpty(children) ? { children } : {}),
|
||||
});
|
||||
if (!mappedNode.children && children) {
|
||||
mappedNode.children = children;
|
||||
}
|
||||
_.set(clonedNodes, pathString, mappedNode);
|
||||
});
|
||||
return clonedNodes;
|
||||
};
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
@bigcapitalhq:registry=https://npm.pkg.github.com
|
||||
@@ -1,17 +1,26 @@
|
||||
FROM node:14.15.0 as build
|
||||
FROM node:18.16.0-alpine as build
|
||||
|
||||
USER root
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies
|
||||
COPY package*.json ./
|
||||
COPY lerna.json ./
|
||||
# Copy application dependency manifests to the container image.
|
||||
COPY ./package*.json ./
|
||||
COPY ./pnpm-lock.yaml ./pnpm-lock.yaml
|
||||
COPY ./pnpm-workspace.yaml ./pnpm-workspace.yaml
|
||||
COPY ./lerna.json ./lerna.json
|
||||
COPY ./packages/webapp/package*.json ./packages/webapp/
|
||||
|
||||
COPY ./packages/webapp/package*.json /app/packages/webapp/
|
||||
# Install application dependencies
|
||||
RUN apk update
|
||||
RUN apk add python3 build-base chromium
|
||||
|
||||
RUN npm install
|
||||
RUN npm run bootstrap
|
||||
# Set PYHTON env
|
||||
ENV PYTHON=/usr/bin/python3
|
||||
|
||||
# Install pnpm packages dependencies
|
||||
RUN npm install -g pnpm
|
||||
RUN pnpm install
|
||||
|
||||
# Build webapp package
|
||||
COPY ./packages/webapp /app/packages/webapp
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const dotenv = require('dotenv-webpack');
|
||||
|
||||
module.exports = {
|
||||
webpack: {
|
||||
plugins: [
|
||||
new dotenv(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
MONOREPO_VERSION: JSON.stringify(require('../../lerna.json').version),
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
},
|
||||
configure: {
|
||||
resolve: {
|
||||
fallback: { path: require.resolve('path-browserify') },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
17754
packages/webapp/package-lock.json
generated
17754
packages/webapp/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,18 @@
|
||||
{
|
||||
"name": "@bigcapital/webapp",
|
||||
"version": "0.9.6",
|
||||
"version": "0.10.2",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@blueprintjs-formik/core": "^0.3.4",
|
||||
"@blueprintjs-formik/datetime": "^0.3.4",
|
||||
"@blueprintjs-formik/select": "^0.3.1",
|
||||
"@blueprintjs/core": "^3.50.2",
|
||||
"@blueprintjs/datetime": "^3.23.12",
|
||||
"@blueprintjs-formik/core": "^0.3.6",
|
||||
"@blueprintjs-formik/datetime": "^0.3.7",
|
||||
"@blueprintjs-formik/select": "^0.3.5",
|
||||
"@blueprintjs/colors": "4.1.19",
|
||||
"@blueprintjs/core": "^4.20.2",
|
||||
"@blueprintjs/datetime": "^4.4.37",
|
||||
"@blueprintjs/popover2": "^0.11.1",
|
||||
"@blueprintjs/select": "^3.11.2",
|
||||
"@blueprintjs/table": "^3.8.3",
|
||||
"@blueprintjs/timezone": "^3.6.2",
|
||||
"@blueprintjs/select": "^4.9.24",
|
||||
"@blueprintjs/table": "^4.10.12",
|
||||
"@blueprintjs/timezone": "^4.5.43",
|
||||
"@casl/ability": "^5.4.3",
|
||||
"@casl/react": "^2.3.0",
|
||||
"@craco/craco": "^5.9.0",
|
||||
@@ -26,6 +27,7 @@
|
||||
"@types/ramda": "^0.28.14",
|
||||
"@types/react": "^16.14.28",
|
||||
"@types/react-body-classname": "^1.1.7",
|
||||
"@types/react-dom": "^16.9.16",
|
||||
"@types/react-redux": "^7.1.24",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-transition-group": "^4.4.5",
|
||||
@@ -38,44 +40,49 @@
|
||||
"axios": "^0.21.2",
|
||||
"basscss": "^8.0.2",
|
||||
"camelcase": "^5.3.1",
|
||||
"classnames": "^2.3.2",
|
||||
"cross-env": "^7.0.2",
|
||||
"deep-map-keys": "^2.0.1",
|
||||
"deepdash": "^5.3.9",
|
||||
"dependency-graph": "^0.11.0",
|
||||
"dotenv-webpack": "^8.0.1",
|
||||
"eslint": "^8.33.0",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"flat": "^5.0.2",
|
||||
"formik": "^2.2.5",
|
||||
"history": "4.10.1",
|
||||
"http-proxy-middleware": "^1.0.0",
|
||||
"jest": "24.9.0",
|
||||
"jest-environment-jsdom-fourteen": "1.0.1",
|
||||
"jest-resolve": "24.9.0",
|
||||
"jest-watch-typeahead": "0.4.2",
|
||||
"js-cookie": "2.2.1",
|
||||
"js-money": "^0.6.3",
|
||||
"lodash": "^4.17.15",
|
||||
"moment": "^2.24.0",
|
||||
"moment-timezone": "^0.5.33",
|
||||
"node-sass": "^4.14.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
"prop-types": "15.8.1",
|
||||
"query-string": "^7.1.1",
|
||||
"ramda": "^0.27.1",
|
||||
"react": "^16.14.0",
|
||||
"react": "^18.2.0",
|
||||
"react-app-polyfill": "^1.0.6",
|
||||
"react-body-classname": "^1.3.1",
|
||||
"react-content-loader": "^6.0.1",
|
||||
"react-dev-utils": "^11.0.4",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dropzone": "^11.0.1",
|
||||
"react-error-boundary": "^3.0.2",
|
||||
"react-error-overlay": "^6.0.9",
|
||||
"react-hotkeys-hook": "^3.0.3",
|
||||
"react-intl-universal": "^2.4.7",
|
||||
"react-loadable": "^5.5.0",
|
||||
"react-query": "^3.6.0",
|
||||
"react-query-devtools": "^2.1.1",
|
||||
"react-redux": "^7.1.3",
|
||||
"react-redux": "^7.2.9",
|
||||
"react-router": "5.3.4",
|
||||
"react-router-breadcrumbs-hoc": "^3.2.10",
|
||||
"react-router-dom": "^5.3.3",
|
||||
"react-scripts": "^3.4.4",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-scroll-sync": "^0.7.1",
|
||||
"react-scrollbars-custom": "^4.0.21",
|
||||
"react-sortablejs": "^2.0.11",
|
||||
@@ -86,11 +93,13 @@
|
||||
"react-use": "^13.26.1",
|
||||
"react-use-context-menu": "^0.1.4",
|
||||
"react-virtualized": "^9.22.3",
|
||||
"redux": "^4.0.5",
|
||||
"redux": "^4.2.1",
|
||||
"redux-devtools": "^3.5.0",
|
||||
"redux-persist": "^6.0.0",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"redux-thunk": "^2.4.2",
|
||||
"reselect": "4.1.7",
|
||||
"rtl-detect": "^1.0.3",
|
||||
"sass": "^1.68.0",
|
||||
"semver": "6.3.0",
|
||||
"style-loader": "0.23.1",
|
||||
"styled-components": "^5.3.1",
|
||||
@@ -105,13 +114,15 @@
|
||||
"storybook": "start-storybook -p 6006"
|
||||
},
|
||||
"proxy": "http://localhost:3000/",
|
||||
"devDependencies": {
|
||||
"@types/react-dom": "^16.9.16",
|
||||
"react-error-overlay": "^6.0.9"
|
||||
},
|
||||
"resolutions": {
|
||||
"react-error-overlay": "6.0.9"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { FSelect } from '@/components/Forms';
|
||||
|
||||
export function AccountsTypesSelect({ ...props }) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
|
||||
// Filters accounts items.
|
||||
export const accountPredicate = (query, account, _index, exactMatch) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { Formik, FastField, FieldArray, useFormikContext } from 'formik';
|
||||
import { Formik, FastField, FieldArray } from 'formik';
|
||||
import {
|
||||
Button,
|
||||
FormGroup,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { Router, Switch, Route } from 'react-router';
|
||||
import { createBrowserHistory } from 'history';
|
||||
import { QueryClientProvider, QueryClient } from 'react-query';
|
||||
|
||||
@@ -18,10 +18,10 @@ export const CardFooterActions = styled.div`
|
||||
border-top: 1px solid #e0e7ea;
|
||||
margin-top: 30px;
|
||||
|
||||
.bp3-button {
|
||||
.bp4-button {
|
||||
min-width: 70px;
|
||||
|
||||
+ .bp3-button {
|
||||
+ .bp4-button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
.menu{
|
||||
:global .bp3-heading{
|
||||
:global .bp4-heading{
|
||||
font-weight: 400;
|
||||
opacity: 0.5;
|
||||
font-size: 12px;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import * as R from 'ramda';
|
||||
import BigcapitalLoading from './BigcapitalLoading';
|
||||
import withDashboard from '@/containers/Dashboard/withDashboard';
|
||||
|
||||
@@ -21,7 +21,7 @@ export function DataTableEditable({
|
||||
}
|
||||
|
||||
const DatatableEditableRoot = styled.div`
|
||||
.bp3-form-group {
|
||||
.bp4-form-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.table {
|
||||
@@ -69,17 +69,17 @@ const DatatableEditableRoot = styled.div`
|
||||
}
|
||||
.tr {
|
||||
&:hover .td,
|
||||
.bp3-input {
|
||||
.bp4-input {
|
||||
background-color: transparent;
|
||||
}
|
||||
.bp3-form-group:not(.bp3-intent-danger) .bp3-input,
|
||||
.form-group--select-list .bp3-button {
|
||||
.bp4-form-group:not(.bp4-intent-danger) .bp4-input,
|
||||
.form-group--select-list .bp4-button {
|
||||
border-color: #ffffff;
|
||||
color: #222;
|
||||
border-radius: 3px;
|
||||
text-align: inherit;
|
||||
}
|
||||
.bp3-form-group:not(.bp3-intent-danger) .bp3-input {
|
||||
.bp4-form-group:not(.bp4-intent-danger) .bp4-input {
|
||||
border-radius: 2px;
|
||||
padding-left: 14px;
|
||||
padding-right: 14px;
|
||||
@@ -88,7 +88,7 @@ const DatatableEditableRoot = styled.div`
|
||||
box-shadow: 0 0 0 2px #116cd0;
|
||||
}
|
||||
}
|
||||
.form-group--select-list .bp3-button {
|
||||
.form-group--select-list .bp4-button {
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
|
||||
@@ -97,16 +97,16 @@ const DatatableEditableRoot = styled.div`
|
||||
}
|
||||
}
|
||||
.form-group--select-list,
|
||||
.bp3-form-group {
|
||||
&.bp3-intent-danger {
|
||||
.bp3-button:not(.bp3-minimal),
|
||||
.bp3-input {
|
||||
.bp4-form-group {
|
||||
&.bp4-intent-danger {
|
||||
.bp4-button:not(.bp4-minimal),
|
||||
.bp4-input {
|
||||
border-color: #f7b6b6;
|
||||
}
|
||||
}
|
||||
}
|
||||
.td.actions {
|
||||
.bp3-button {
|
||||
.bp4-button {
|
||||
color: #80858f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ const DialogFooterActionsRoot = styled.div`
|
||||
${(props) =>
|
||||
props.alignment === 'right' ? 'margin-left: auto;' : 'margin-right: auto;'};
|
||||
|
||||
.bp3-button {
|
||||
.bp4-button {
|
||||
margin-left: 5px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import React from 'react';
|
||||
|
||||
import AccountDialog from '@/containers/Dialogs/AccountDialog';
|
||||
import InviteUserDialog from '@/containers/Dialogs/InviteUserDialog';
|
||||
import UserFormDialog from '@/containers/Dialogs/UserFormDialog';
|
||||
|
||||
@@ -17,8 +17,8 @@ export function DrawerMainTabs({ children, ...restProps }) {
|
||||
}
|
||||
|
||||
const DrawerMainTabsRoot = styled.div`
|
||||
.bp3-tabs {
|
||||
.bp3-tab-list {
|
||||
.bp4-tabs {
|
||||
.bp4-tab-list {
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
padding: 0 15px;
|
||||
@@ -28,7 +28,7 @@ const DrawerMainTabsRoot = styled.div`
|
||||
margin-right: 25px;
|
||||
}
|
||||
|
||||
&.bp3-large > .bp3-tab {
|
||||
&.bp4-large > .bp4-tab {
|
||||
font-size: 15px;
|
||||
color: #7f8596;
|
||||
margin: 0 1rem;
|
||||
@@ -38,13 +38,13 @@ const DrawerMainTabsRoot = styled.div`
|
||||
color: #0052cc;
|
||||
}
|
||||
}
|
||||
.bp3-tab-indicator-wrapper .bp3-tab-indicator {
|
||||
.bp4-tab-indicator-wrapper .bp4-tab-indicator {
|
||||
height: 2px;
|
||||
bottom: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
.bp3-tab-panel {
|
||||
.bp4-tab-panel {
|
||||
margin-top: 0;
|
||||
|
||||
.card {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import React from 'react';
|
||||
|
||||
import AccountDrawer from '@/containers/Drawers/AccountDrawer';
|
||||
import ManualJournalDrawer from '@/containers/Drawers/ManualJournalDrawer';
|
||||
import ExpenseDrawer from '@/containers/Drawers/ExpenseDrawer';
|
||||
|
||||
@@ -60,8 +60,8 @@ export function ExchangeRateMutedField({
|
||||
}
|
||||
|
||||
const ExchangeRateFormGroup = styled(FormGroup)`
|
||||
&.bp3-form-group {
|
||||
label.bp3-label {
|
||||
&.bp4-form-group {
|
||||
label.bp4-label {
|
||||
font-size: 12px;
|
||||
opacity: 0.7;
|
||||
line-height: 1;
|
||||
@@ -79,7 +79,7 @@ const ExchangeRateButton = styled.div`
|
||||
position: relative;
|
||||
padding-right: 28px;
|
||||
|
||||
.bp3-button {
|
||||
.bp4-button {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
@@ -88,7 +88,7 @@ const ExchangeRateButton = styled.div`
|
||||
const ExchangeRateFormGroupContent = styled.div`
|
||||
padding: 5px 0;
|
||||
|
||||
.bp3-form-group {
|
||||
.bp4-form-group {
|
||||
padding: 2px;
|
||||
margin: 2px 4px !important;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,11 @@ const SelectButton = styled(Button)`
|
||||
position: relative;
|
||||
padding-right: 30px;
|
||||
|
||||
&:not(.is-selected):not([class*='bp3-intent-']):not(.bp3-minimal) {
|
||||
&.bp4-small{
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
&:not(.is-selected):not([class*='bp4-intent-']):not(.bp4-minimal) {
|
||||
color: #5c7080;
|
||||
}
|
||||
&:after {
|
||||
@@ -46,13 +50,13 @@ const SelectButton = styled(Button)`
|
||||
margin-right: 12px;
|
||||
border-radius: 1px;
|
||||
}
|
||||
&:not([class*='bp3-intent-']):not(.bp3-disabled) {
|
||||
&:not([class*='bp4-intent-']):not(.bp4-disabled) {
|
||||
&,
|
||||
&:hover {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
.bp3-intent-danger & {
|
||||
.bp4-intent-danger & {
|
||||
border-color: #db3737;
|
||||
}
|
||||
`;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user