Compare commits

..

1 Commits

Author SHA1 Message Date
a.bouhuolia
813bed3676 chore: add CONTRIBUTING file 2023-04-27 01:50:09 +02:00
1138 changed files with 19001 additions and 22288 deletions

View File

@@ -1,71 +0,0 @@
{
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"commitType": "docs",
"commitConvention": "angular",
"contributors": [
{
"login": "abouolia",
"name": "Ahmed Bouhuolia",
"avatar_url": "https://avatars.githubusercontent.com/u/2197422?v=4",
"profile": "https://github.com/abouolia",
"contributions": [
"code"
]
},
{
"login": "ameir",
"name": "Ameir Abdeldayem",
"avatar_url": "https://avatars.githubusercontent.com/u/374330?v=4",
"profile": "http://ameir.net",
"contributions": [
"bug"
]
},
{
"login": "elforjani13",
"name": "ElforJani13",
"avatar_url": "https://avatars.githubusercontent.com/u/39470382?v=4",
"profile": "https://github.com/elforjani13",
"contributions": [
"code"
]
},
{
"login": "scheibling",
"name": "Lars Scheibling",
"avatar_url": "https://avatars.githubusercontent.com/u/24367830?v=4",
"profile": "https://scheibling.se",
"contributions": [
"bug"
]
},
{
"login": "suhaibaffan",
"name": "Suhaib Affan",
"avatar_url": "https://avatars.githubusercontent.com/u/18115937?v=4",
"profile": "https://github.com/suhaibaffan",
"contributions": [
"code"
]
},
{
"login": "KalliopiPliogka",
"name": "Kalliopi Pliogka",
"avatar_url": "https://avatars.githubusercontent.com/u/81677549?v=4",
"profile": "https://github.com/KalliopiPliogka",
"contributions": [
"bug"
]
}
],
"contributorsPerLine": 7,
"skipCi": true,
"repoType": "github",
"repoHost": "https://github.com",
"projectName": "bigcapital",
"projectOwner": "bigcapitalhq"
}

View File

@@ -8,45 +8,27 @@ MAIL_FROM_NAME=
MAIL_FROM_ADDRESS=
# Database
DB_HOST=mysql
DB_USER=bigcapital
DB_PASSWORD=bigcapital
DB_ROOT_PASSWORD=root
DB_CHARSET=utf8
DB_USER=
DB_HOST=
DB_PASSWORD=
DB_CHARSET=
# System database
SYSTEM_DB_NAME=bigcapital_system
# SYSTEM_DB_USER=
# SYSTEM_DB_PASSWORD=
# SYSTEM_DB_NAME=
# SYSTEM_DB_CHARSET=
# Tenant databases
# Tenants databases
TENANT_DB_NAME_PERFIX=bigcapital_tenant_
# TENANT_DB_HOST=
# TENANT_DB_USER=
# TENANT_DB_PASSWORD=
# TENANT_DB_CHARSET=
# Application
BASE_URL=http://example.com
JWT_SECRET=b0JDZW56RnV6aEthb0RGPXVEcUI
# Jobs MongoDB
# MongoDB
MONGODB_DATABASE_URL=mongodb://localhost/bigcapital
# App proxy
PUBLIC_PROXY_PORT=80
PUBLIC_PROXY_SSL_PORT=443
# Authentication
JWT_SECRET=b0JDZW56RnV6aEthb0RGPXVEcUI
# Application
BASE_URL=https://bigcapital.ly
CONTACT_US_MAIL=support@bigcapital.ly
# Agendash
AGENDASH_AUTH_USER=agendash
AGENDASH_AUTH_PASSWORD=123123
# Sign-up restrictions
SIGNUP_DISABLED=false
SIGNUP_ALLOWED_DOMAINS=
SIGNUP_ALLOWED_EMAILS=
# API rate limit (points,duration,block duration).
API_RATE_LIMIT=120,60,600

2
.gitattributes vendored
View File

@@ -1,2 +0,0 @@
docker/nginx/scripts/build-nginx.sh text eol=lf
docker/mariadb/docker-entrypoint.sh text eol=lf

13
.github/FUNDING.yml vendored
View File

@@ -1,13 +0,0 @@
# 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']

7
.gitignore vendored
View File

@@ -1,9 +1,4 @@
node_modules/
# Docker volumes data directory
/data
# Production env file
data
.env
test-results/

View File

@@ -2,125 +2,6 @@
All notable changes to Bigcapital server-side will be in this file.
# [0.9.12] - 29-08-2023
* Refactor: split the services to multiple service classes. (by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/202)
* Fix: create quick customer/vendor by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/206
* Fix: typo in bill success message without bill number by @KalliopiPliogka in https://github.com/bigcapitalhq/bigcapital/pull/219
* Fix: AP/AR aging summary issue by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/229
* Fix: shouldn't write GL entries when save transaction as draft. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/221
* Fix: Transaction type of credit note and vendor credit are not defined on account transactions by @abouolia in
* Fix: date format of filtering transactions by date range by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/231
* Fix: change the default from/date date value of reports by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/230
* Fix: typos in words start with `A` letter by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/227
* Fix: filter by customers, vendors and items in reports do not work by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/224
https://github.com/bigcapitalhq/bigcapital/pull/225
# [0.9.11] - 23-07-2023
* added: Restart policy to docker compose files. by @suhaibaffan in https://github.com/bigcapitalhq/bigcapital/pull/198
* fix: Expose and expand the rate limit to the env variables by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/195
# [0.9.10] - 18-07-2023
* feat(e2e): E2E onboarding process by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/176
* fix(webapp): Show loading message of cost computing job on financial reports by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/196
* fix(webapp): Change the currency code of sales and purchases transactions with foreign contacts.
# [0.9.9] - 28-06-2023
* refactor: Customer and vendor select component by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/171
* chore: Move auto-increment components in separate files by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/170
* fix: Style of quick item drawer by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/173
* fix: Should not show the form before loading account by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/172
* fix: Payment made form does not handle not unique number an e… by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/177
* fix: Internal note of invoice/bill payment does not saving by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/181
* fix: Storing cash flow transaction description by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/180
* fix: No currency in amount field on money in/out dialogs by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/179
* fix: No default branch for customer/vendor opening balance branch by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/182
# [0.9.8] - 19-06-2023
`bigcapitalhq/webapp`
* add: Inventory Adjustment option to the item drawer by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/158
* fix: use all drawers names from common enum object by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/157
* fix: adjustment type options do not show up by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/159
* fix: change the remove line text to be red to intent as a danger action by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/162
* fix: rename sidebar localization keys names to be keyword path by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/161
* fix: manual journal placeholder text by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/160
* fix: warehouses select component by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/168
`bigcapitalhq/server`
* fix: sending emails on reset password and registration by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/167
## [0.9.7] - 14-06-2023
`@bigcapital/webapp`
* fix: change the footer links of onboarding pages by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/139
`@bigcapital/server`
* fix: expense transaction journal entries by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/155
## [0.9.6] - 12-06-2023
`@bigcapital/webapp`
* fix: remove duplicated form submitting by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/138
* feat: add monorepo version on the application sidebar by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/136
## [0.9.5] - 11-06-2023
`@bigcapital/server`
* fix: filter ledger entries that effect contact balance to AR/AP accounts only by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/132
`@bigcapital/webapp`
* fix: catch journal error when create a journal with accounts have different currency by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/135
* fix: add duplicate icon to context menu of customers and vendors table by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/133
* fix: customer/vendor opening balance with exchange rate by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/134
## [0.9.4] - 08-06-2023
`@bigcapital/monorepo`
- fixed: docker-compose line-ending issue on Windows by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/130
`@bigcapital/server`
- fixed: Disable Webpack minification for JS class name reading.
## [0.9.3] -04-06-2023
`@bigcapital/monorepo`
* Added: Add env variable to customize the proxy public ports by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/125
* Added: Migrate the server database to MariaDB by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/128
## [0.9.2] - 31-05-2023
`@bigcapital/webapp`
- fixed: move `packaeg-lock.json` inside docker container.
- fixed: remove Sentry from the web client.
## [0.9.1] - 28-05-2023
`@bigcapital/server`
- fix: deleting ledger entries of manual journal.
- fix: base currency should be enabled.
- fix: delete invoice transaction issue.
`@bigcapital/webapp`
- fix: general, accountant and items preferences.
- fix: auto-increment sale invoices, estiamtes, credit notes, payments and manual journals.
- refactor: the setup organization form to use binded Formik components.
## [0.9.0] - 06-05-2023
`@bigcapital/server`
- [Sign-up restrictions](https://docs.bigcapital.ly/docs/deployment/signup_restriction) for self-hosting instances to disable signup or control the allowed email addresses and domains that can sign-up.
## [0.8.3] - 06-04-2023
`@bigcaptial/monorepo`

View File

@@ -7,7 +7,6 @@ Please read through this document before submitting any issues or pull requests
## Sections
- [General Instructions](#general-instructions)
- [Local Setup Prerequisites](#local-setup-prerequisites)
- [Contribute to Backend](#contribute-to-backend)
- [Contribute to Frontend](#contribute-to-frontend)
- [Other Ways to Contribute](#other-ways-to-contribute)
@@ -32,19 +31,10 @@ 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)
## Contribute to Backend
- Clone the `bigcapital` repository and `cd` into `bigcapital` directory.
- Create `.env` file by copying `.env.example` file to `.env`. (The ``.env.example`` file has all the necessary values of variables to start development directly).
```
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
@@ -57,7 +47,7 @@ npm run bootstrap
docker-compose up -d
```
Wait some seconds, and hit `docker-compose ps` and you should see the same result below.
Wait some seconds, and hit `docker-compose ps` to see the result and you should see the same result below.
```
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
@@ -132,7 +122,7 @@ There are many other ways to get involved with the community and to participate
- Use the product, submitting GitHub issues when a problem is found.
- Help code review pull requests and participate in issue threads.
- Submit a new feature request as an issue.
- Help answer questions on forums such as Bigcapital Community Discord Channel.
- Help answer questions on forums such as Stack Overflow and SigNoz Community Slack Channel.
- Tell others about the project on Twitter, your blog, etc.
**[`^top^`](#)**

View File

@@ -7,24 +7,6 @@
<p align="center">
Simple, smart online accounting software for small and medium businesses.
</p>
<p align="center">
<a href="https://github.com/bigcapitalhq/bigcapital/commits/develop">
<img src="https://img.shields.io/github/commit-activity/m/bigcapitalhq/bigcapital/develop" />
</a>
<a href="https://discord.com/invite/c8nPBJafeb">
<img src="https://img.shields.io/discord/1066514716752625725?label=Discord" alt="" />
</a>
<a href="https://github.com/bigcapitalhq/bigcapital/graphs/contributors">
<img src="https://img.shields.io/github/contributors/bigcapitalhq/bigcapital" alt="" />
</a>
<a href="https://github.com/bigcapitalhq/bigcapital/blob/develop/LICENSE">
<img src="https://img.shields.io/github/license/bigcapitalhq/bigcapital" alt="" />
</a>
<a href="https://twitter.com/bigcapitalhq">
<img src="https://img.shields.io/twitter/follow/bigcapitalhq?style=social" alt="twitter" />
</a>
</p>
</p>
# What's Bigcapital?
@@ -40,53 +22,10 @@ Bigcapital is a smart and open-source accounting and inventory software, Bigcapi
# Resources
- [Documentation](https://docs.bigcapital.ly/) - Learn how to use.
- [Contribution](https://github.com/bigcapitalhq/bigcapital/blob/develop/CONTRIBUTING.md) - Welcome to any contributions.
- [Discord](https://discord.com/invite/c8nPBJafeb) - Ask for help.
- [Bug Tracker](https://github.com/bigcapitalhq/bigcapital/issues) - Notify us new bugs.
- [Source Code](https://github.com/bigcapitalhq/bigcapital) - Github repo.
# Changelog
# Changlog
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">
<img
style="width: 250px; height: 54px;" width="250" height="54"
alt="Featured on Hacker News"
src="https://hackernews-badge.vercel.app/api?id=36118990"
/>
</a>
# Contributors
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tbody>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/abouolia"><img src="https://avatars.githubusercontent.com/u/2197422?v=4?s=100" width="100px;" alt="Ahmed Bouhuolia"/><br /><sub><b>Ahmed Bouhuolia</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=abouolia" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://ameir.net"><img src="https://avatars.githubusercontent.com/u/374330?v=4?s=100" width="100px;" alt="Ameir Abdeldayem"/><br /><sub><b>Ameir Abdeldayem</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Aameir" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/elforjani13"><img src="https://avatars.githubusercontent.com/u/39470382?v=4?s=100" width="100px;" alt="ElforJani13"/><br /><sub><b>ElforJani13</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=elforjani13" title="Code">💻</a></td>
<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>
</tr>
</tbody>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

View File

@@ -15,22 +15,16 @@ services:
- ./data/logs/nginx/:/var/log/nginx
- ./docker/certbot/certs/:/var/certs
ports:
- '${PUBLIC_PROXY_PORT:-80}:80'
- '${PUBLIC_PROXY_SSL_PORT:-443}:443'
- "80:80"
- "443:443"
tty: true
depends_on:
- server
- webapp
deploy:
restart_policy:
condition: unless-stopped
webapp:
webapp:
container_name: bigcapital-webapp
image: ghcr.io/bigcapitalhq/webapp:latest
deploy:
restart_policy:
condition: unless-stopped
server:
container_name: bigcapital-server
@@ -43,9 +37,6 @@ services:
- mysql
- mongo
- redis
deploy:
restart_policy:
condition: unless-stopped
environment:
# Mail
- MAIL_HOST=${MAIL_HOST}
@@ -71,7 +62,7 @@ services:
# Authentication
- JWT_SECRET=${JWT_SECRET}
# MongoDB
# MongoDB
- MONGODB_DATABASE_URL=mongodb://mongo/bigcapital
# Application
@@ -81,50 +72,36 @@ services:
- AGENDASH_AUTH_USER=${AGENDASH_AUTH_USER}
- AGENDASH_AUTH_PASSWORD=${AGENDASH_AUTH_PASSWORD}
# Sign-up restrictions
- SIGNUP_DISABLED=${SIGNUP_DISABLED}
- SIGNUP_ALLOWED_DOMAINS=${SIGNUP_ALLOWED_DOMAINS}
- SIGNUP_ALLOWED_EMAILS=${SIGNUP_ALLOWED_EMAILS}
database_migration:
container_name: bigcapital-database-migration
build:
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
mysql:
container_name: bigcapital-mysql
deploy:
restart_policy:
condition: unless-stopped
build:
context: ./docker/mariadb
context: ./docker/mysql
environment:
- MYSQL_DATABASE=${SYSTEM_DB_NAME}
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASSWORD}
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
volumes:
- mysql:/var/lib/mysql
expose:
- '3306'
mongo:
container_name: bigcapital-mongo
deploy:
restart_policy:
condition: unless-stopped
container_name: bigcapital-mongo
build: ./docker/mongo
expose:
- '27017'
@@ -133,13 +110,10 @@ services:
redis:
container_name: bigcapital-redis
deploy:
restart_policy:
condition: unless-stopped
build:
context: ./docker/redis
expose:
- '6379'
- "6379"
volumes:
- redis:/data

View File

@@ -6,23 +6,20 @@
version: '3.3'
services:
mariadb:
mysql:
build:
context: ./docker/mariadb
context: ./docker/mysql
environment:
- MYSQL_DATABASE=${SYSTEM_DB_NAME}
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASSWORD}
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
volumes:
- mysql:/var/lib/mysql
expose:
- '3306'
ports:
- '3306:3306'
deploy:
restart_policy:
condition: unless-stopped
mongo:
build: ./docker/mongo
@@ -32,9 +29,6 @@ services:
- mongo:/var/lib/mongodb
ports:
- '27017:27017'
deploy:
restart_policy:
condition: unless-stopped
redis:
build:
@@ -43,9 +37,6 @@ services:
- "6379"
volumes:
- redis:/data
deploy:
restart_policy:
condition: unless-stopped
# Volumes
volumes:

View File

@@ -34,7 +34,5 @@ WORKDIR /app/packages/server
RUN git clone https://github.com/vishnubob/wait-for-it.git
ADD docker/migration/start.sh /
RUN chmod +x /start.sh
CMD ["/start.sh"]
# 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"]

View File

@@ -1,5 +0,0 @@
# 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

View File

@@ -1,4 +1,4 @@
FROM mariadb:10.2
FROM mysql:5.7
USER root
ADD my.cnf /etc/mysql/conf.d/my.cnf
@@ -17,7 +17,7 @@ ENV MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD
COPY ./init.sql /scripts/init.template.sql
COPY ./docker-entrypoint.sh /docker-entrypoint-initdb.d/docker-initialize.sh
# The scripts in the `docker-entrypoint-initdb.d/` directory are executed as
# The scripts in the docker-entrypoint-initdb.d/ directory are executed as
# the mysql user inside the MySQL Docker container.
RUN chown -R mysql:root /docker-entrypoint-initdb.d
RUN chown -R mysql:root /scripts

View File

@@ -1,3 +1,2 @@
GRANT ALL PRIVILEGES ON *.* TO '{MYSQL_USER}'@'%' IDENTIFIED BY '{MYSQL_PASSWORD}' WITH GRANT OPTION;
FLUSH PRIVILEGES;

View File

@@ -1,13 +0,0 @@
import { Page } from '@playwright/test';
export const clearLocalStorage = (page: Page) => {
return page.evaluate(`window.localStorage.clear()`);
};
export const defaultPageConfig = () => {
return {
extraHTTPHeaders: {
'ngrok-skip-browser-warning': 'any-value',
},
};
};

View File

@@ -1,23 +1,14 @@
import { test, expect, Page } from '@playwright/test';
import { faker } from '@faker-js/faker';
import { clearLocalStorage, defaultPageConfig } from './_utils';
let authPage: Page;
test.describe('authentication', () => {
test.beforeAll(async ({ browser }) => {
authPage = await browser.newPage({ ...defaultPageConfig() });
});
test.afterAll(async () => {
await authPage.close();
});
test.afterEach(async ({ context }) => {
context.clearCookies();
await clearLocalStorage(authPage);
authPage = await browser.newPage();
});
test.describe('login', () => {
test.beforeEach(async () => {
test.beforeAll(async () => {
await authPage.goto('/auth/login');
});
test('should show the login page.', async () => {
@@ -39,23 +30,10 @@ test.describe('authentication', () => {
await authPage.getByRole('link', { name: 'Sign up' }).click();
await expect(authPage.url()).toContain('/auth/register');
});
test('should the email or password is not correct.', async () => {
await authPage.getByLabel('Email Address').click();
await authPage.getByLabel('Email Address').fill(faker.internet.email());
await authPage.getByLabel('Password').click();
await authPage.getByLabel('Password').fill(faker.internet.password());
await authPage.getByRole('button', { name: 'Log in' }).click();
await expect(authPage.locator('body')).toContainText(
'The email and password you entered did not match our records.'
);
});
});
test.describe('register', () => {
test.beforeEach(async () => {
test.beforeAll(async () => {
await authPage.goto('/auth/register');
});
test('should first name, last name, email and password be required.', async () => {
@@ -74,36 +52,10 @@ test.describe('authentication', () => {
'Password is a required field'
);
});
test('should signup successfully.', async () => {
const form = authPage.locator('form');
await form.getByLabel('First Name').click();
await form.getByLabel('First Name').fill(faker.person.firstName());
await form.getByLabel('Email').click();
await form.getByLabel('Email').fill(faker.internet.email());
await form.getByLabel('Last Name').click();
await form.getByLabel('Last Name').fill(faker.person.lastName());
await form.getByLabel('Password').click();
await form.getByLabel('Password').fill(faker.internet.password());
await authPage.getByRole('button', { name: 'Register' }).click();
await expect(authPage.locator('h1')).toContainText(
'Register a New Organization now!'
);
});
});
test.describe('reset password', () => {
test.beforeAll(async ({ browser }) => {
authPage = await browser.newPage({ ...defaultPageConfig() });
});
test.afterAll(async () => {
await authPage.close();
});
test.beforeEach(async () => {
test.beforeAll(async () => {
await authPage.goto('/auth/send_reset_password');
});
test('should email be required.', async () => {

View File

@@ -1,7 +0,0 @@
import { test, expect, Page } from '@playwright/test';
test.describe('item', () => {
test('should validate all required fields.', () => {});
test('should save the item successfully.', () => {});
test('should item code be unqiue.', () => {});
});

View File

@@ -1,86 +0,0 @@
import { test, expect, Page } from '@playwright/test';
import { faker } from '@faker-js/faker';
import { defaultPageConfig } from './_utils';
let authPage: Page;
let businessLegalName: string = faker.company.name();
test.describe('onboarding', () => {
test.beforeAll(async ({ browser }) => {
authPage = await browser.newPage({ ...defaultPageConfig() });
await authPage.goto('/auth/register');
const form = authPage.locator('form');
await form.getByLabel('First Name').fill(faker.person.firstName());
await form.getByLabel('Email').fill(faker.internet.email());
await form.getByLabel('Last Name').fill(faker.person.lastName());
await form.getByLabel('Password').fill(faker.internet.password());
await authPage.getByRole('button', { name: 'Register' }).click();
});
test('should validation catch all required fields', async () => {
const form = authPage.locator('form');
await authPage.getByRole('button', { name: 'Save & Continue' }).click();
await expect(form).toContainText('Organization name is a required field');
await expect(form).toContainText('Location is a required field');
await expect(form).toContainText('Base currency is a required field');
await expect(form).toContainText('Fiscal year is a required field');
await expect(form).toContainText('Time zone is a required field');
});
test.describe('after onboarding', () => {
test.beforeAll(async () => {
await authPage.getByLabel('Legal Organization Name').click();
await authPage
.getByLabel('Legal Organization Name')
.fill(businessLegalName);
// Fill Business Location.
await authPage
.getByRole('button', { name: 'Select Business Location...' })
.click();
await authPage.locator('a').filter({ hasText: 'Albania' }).click();
// Fill Base Currency.
await authPage
.getByRole('button', { name: 'Select Base Currency...' })
.click();
await authPage
.locator('a')
.filter({ hasText: 'AED - United Arab Emirates Dirham' })
.click();
// Fill Fasical Year.
await authPage
.getByRole('button', { name: 'Select Fiscal Year...' })
.click();
await authPage.locator('a').filter({ hasText: 'June - May' }).click();
// Fill Timezone.
await authPage
.getByRole('button', { name: 'Select Time Zone...' })
.click();
await authPage.getByText('Pacific/Marquesas-09:30').click();
// Click on Submit button
await authPage.getByRole('button', { name: 'Save & Continue' }).click();
});
test('should onboarding process success', async () => {
await expect(authPage.locator('body')).toContainText(
'Congrats! You are ready to go',
{
timeout: 30000,
}
);
});
test('should go to the dashboard after clicking on "Go to dashboard" button.', async () => {
await authPage.getByRole('button', { name: 'Go to dashboard' }).click();
await expect(
authPage.locator('[data-testId="dashboard-topbar"] h1')
).toContainText(businessLegalName);
});
});
});

View File

@@ -1,6 +1,6 @@
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"useWorkspaces": true,
"version": "0.9.6",
"version": "0.0.0",
"npmClient": "npm"
}

14
package-lock.json generated
View File

@@ -333,12 +333,6 @@
"@jridgewell/trace-mapping": "0.3.9"
}
},
"@faker-js/faker": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.0.2.tgz",
"integrity": "sha512-Uo3pGspElQW91PCvKSIAXoEgAUlRnH29sX2/p89kg7sP1m2PzCufHINd0FhTXQf6DYGiUlVncdSPa2F9wxed2A==",
"dev": true
},
"@gar/promisify": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
@@ -951,7 +945,6 @@
"version": "1.32.3",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.3.tgz",
"integrity": "sha512-BvWNvK0RfBriindxhLVabi8BRe3X0J9EVjKlcmhxjg4giWBD/xleLcg2dz7Tx0agu28rczjNIPQWznwzDwVsZQ==",
"dev": true,
"requires": {
"@types/node": "*",
"fsevents": "2.3.2",
@@ -961,8 +954,7 @@
"playwright-core": {
"version": "1.32.3",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.3.tgz",
"integrity": "sha512-SB+cdrnu74ZIn5Ogh/8278ngEh9NEEV0vR4sJFmK04h2iZpybfbqBY0bX6+BLYWVdV12JLLI+JEFtSnYgR+mWg==",
"dev": true
"integrity": "sha512-SB+cdrnu74ZIn5Ogh/8278ngEh9NEEV0vR4sJFmK04h2iZpybfbqBY0bX6+BLYWVdV12JLLI+JEFtSnYgR+mWg=="
}
}
},
@@ -1011,8 +1003,7 @@
"@types/node": {
"version": "18.14.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.6.tgz",
"integrity": "sha512-93+VvleD3mXwlLI/xASjw0FzKcwzl3OdTCzm1LaRfqgS21gfFtK3zDXM5Op9TeeMsJVOaJ2VRDpT9q4Y3d0AvA==",
"dev": true
"integrity": "sha512-93+VvleD3mXwlLI/xASjw0FzKcwzl3OdTCzm1LaRfqgS21gfFtK3zDXM5Op9TeeMsJVOaJ2VRDpT9q4Y3d0AvA=="
},
"@types/normalize-package-data": {
"version": "2.4.1",
@@ -2333,7 +2324,6 @@
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"optional": true
},
"function-bind": {

View File

@@ -18,13 +18,12 @@
"shared/*"
],
"devDependencies": {
"@commitlint/cli": "^17.4.2",
"@commitlint/config-conventional": "^17.4.2",
"@commitlint/config-lerna-scopes": "^17.4.2",
"@faker-js/faker": "^8.0.2",
"@playwright/test": "^1.32.3",
"husky": "^8.0.3",
"lerna": "^6.4.1"
"lerna": "^6.4.1",
"@commitlint/cli": "^17.4.2",
"@playwright/test": "^1.32.3"
},
"engines": {
"node": "14.x"

View File

@@ -34,11 +34,7 @@ ARG MAIL_HOST= \
BASE_URL= \
# Agendash
AGENDASH_AUTH_USER=agendash \
AGENDASH_AUTH_PASSWORD=123123 \
# Sign-up restriction
SIGNUP_DISABLED= \
SIGNUP_ALLOWED_DOMAINS= \
SIGNUP_ALLOWED_EMAILS=
AGENDASH_AUTH_PASSWORD=123123
ENV MAIL_HOST=$MAIL_HOST \
MAIL_USERNAME=$MAIL_USERNAME \
@@ -72,11 +68,7 @@ ENV MAIL_HOST=$MAIL_HOST \
# MongoDB
MONGODB_DATABASE_URL=$MONGODB_DATABASE_URL \
# Application
BASE_URL=$BASE_URL \
# Sign-up restriction
SIGNUP_DISABLED=$SIGNUP_DISABLED \
SIGNUP_ALLOWED_DOMAINS=$SIGNUP_ALLOWED_DOMAINS \
SIGNUP_ALLOWED_EMAILS=$SIGNUP_ALLOWED_EMAILS
BASE_URL=$BASE_URL
# Create app directory.
WORKDIR /app

View File

@@ -1,6 +1,6 @@
{
"name": "@bigcapital/server",
"version": "0.10.0",
"version": "1.7.1",
"description": "",
"main": "src/server.ts",
"scripts": {

View File

@@ -152,7 +152,7 @@
"Opening Balance Liabilities": "رصيد الالتزامات الافتتاحي",
"Loan": "اقراض",
"Owner A Drawings": "مسحوبات المالك",
"An account that holds valuation of products or goods that available for sale.": "حساب يحمل قيم مخزون البضاعة أو السلع المتاحة للبيع.",
"An account that holds valuation of products or goods that availiable for sale.": "حساب يحمل قيم مخزون البضاعة أو السلع المتاحة للبيع.",
"Tracks the gain and losses of the exchange differences.": "يسجل مكاسب وخسائر فروق الصرف.",
"Any bank fees levied is recorded into the bank fees and charges account. A bank account maintenance fee, transaction charges, a late payment fee are some examples.": "يتم تسجيل أي رسوم مصرفية يتم فرضها في حساب الرسوم والمصروفات البنكية. ومن الأمثلة على ذلك رسوم صيانة الحساب المصرفي ورسوم المعاملات ورسوم الدفع المتأخر.",
"The income activities are not associated to the core business.": "لا ترتبط انشطة الدخل إلى الأعمال الأساسية.",

View File

@@ -151,7 +151,7 @@
"Opening Balance Liabilities": "Opening Balance Liabilities",
"Loan": "Loan",
"Owner A Drawings": "Owner A Drawings",
"An account that holds valuation of products or goods that available for sale.": "An account that holds valuation of products or goods that available for sale.",
"An account that holds valuation of products or goods that availiable for sale.": "An account that holds valuation of products or goods that availiable for sale.",
"Tracks the gain and losses of the exchange differences.": "Tracks the gain and losses of the exchange differences.",
"Any bank fees levied is recorded into the bank fees and charges account. A bank account maintenance fee, transaction charges, a late payment fee are some examples.": "Any bank fees levied is recorded into the bank fees and charges account. A bank account maintenance fee, transaction charges, a late payment fee are some examples.",
"The income activities are not associated to the core business.": "The income activities are not associated to the core business.",

View File

@@ -65,9 +65,6 @@ exports.getCommonWebpackOptions = ({
},
],
},
optimization: {
minimize: false,
},
};
if (isDev) {

View File

@@ -3,12 +3,7 @@ import { check, param, query } from 'express-validator';
import { Service, Inject } from 'typedi';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import BaseController from '@/api/controllers/BaseController';
import {
AbilitySubject,
AccountAction,
IAccountDTO,
IAccountsStructureType,
} from '@/interfaces';
import { AbilitySubject, AccountAction, IAccountDTO } from '@/interfaces';
import { ServiceError } from '@/exceptions';
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
import { DATATYPES_LENGTH } from '@/data/DataTypes';
@@ -177,11 +172,6 @@ export default class AccountsController extends BaseController {
query('inactive_mode').optional().isBoolean().toBoolean(),
query('search_keyword').optional({ nullable: true }).isString().trim(),
query('structure')
.optional()
.isString()
.isIn([IAccountsStructureType.Tree, IAccountsStructureType.Flat]),
];
}
@@ -351,7 +341,6 @@ export default class AccountsController extends BaseController {
sortOrder: 'desc',
columnSortBy: 'created_at',
inactiveMode: false,
structure: IAccountsStructureType.Tree,
...this.matchedQueryData(req),
};

View File

@@ -49,7 +49,6 @@ export default class AuthenticationController extends BaseController {
asyncMiddleware(this.resetPassword.bind(this)),
this.handlerErrors
);
router.get('/meta', asyncMiddleware(this.getAuthMeta.bind(this)));
return router;
}
@@ -92,7 +91,6 @@ export default class AuthenticationController extends BaseController {
check('password')
.exists()
.isString()
.isLength({ min: 6 })
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
@@ -107,7 +105,7 @@ export default class AuthenticationController extends BaseController {
return [
check('password')
.exists()
.isLength({ min: 6 })
.isLength({ min: 5 })
.custom((value, { req }) => {
if (value !== req.body.confirm_password) {
throw new Error("Passwords don't match");
@@ -209,23 +207,6 @@ export default class AuthenticationController extends BaseController {
}
}
/**
* Retrieves the authentication meta for SPA.
* @param {Request} req
* @param {Response} res
* @param {Function} next
* @returns {Response|void}
*/
private async getAuthMeta(req: Request, res: Response, next: Function) {
try {
const meta = await this.authApplication.getAuthMeta();
return res.status(200).send({ meta });
} catch (error) {
next(error);
}
}
/**
* Handles the service errors.
*/
@@ -266,30 +247,6 @@ export default class AuthenticationController extends BaseController {
errors: [{ type: 'EMAIL.EXISTS', code: 600 }],
});
}
if (error.errorType === 'SIGNUP_RESTRICTED') {
return res.status(400).send({
errors: [
{
type: 'SIGNUP_RESTRICTED',
message:
'Sign-up is restricted no one can sign-up to the system.',
code: 700,
},
],
});
}
if (error.errorType === 'SIGNUP_RESTRICTED_NOT_ALLOWED') {
return res.status(400).send({
errors: [
{
type: 'SIGNUP_RESTRICTED_NOT_ALLOWED',
message:
'Sign-up is restricted the given email address is not allowed to sign-up.',
code: 710,
},
],
});
}
}
next(error);
}

View File

@@ -20,7 +20,6 @@ import InventoryDetailsController from './FinancialStatements/InventoryDetails';
import TransactionsByReferenceController from './FinancialStatements/TransactionsByReference';
import CashflowAccountTransactions from './FinancialStatements/CashflowAccountTransactions';
import ProjectProfitabilityController from './FinancialStatements/ProjectProfitabilitySummary';
import SalesTaxLiabilitySummary from './FinancialStatements/SalesTaxLiabilitySummary';
@Service()
export default class FinancialStatementsService {
@@ -69,44 +68,40 @@ export default class FinancialStatementsService {
);
router.use(
'/customer-balance-summary',
Container.get(CustomerBalanceSummaryController).router()
Container.get(CustomerBalanceSummaryController).router(),
);
router.use(
'/vendor-balance-summary',
Container.get(VendorBalanceSummaryController).router()
Container.get(VendorBalanceSummaryController).router(),
);
router.use(
'/transactions-by-customers',
Container.get(TransactionsByCustomers).router()
Container.get(TransactionsByCustomers).router(),
);
router.use(
'/transactions-by-vendors',
Container.get(TransactionsByVendors).router()
Container.get(TransactionsByVendors).router(),
);
router.use(
'/cash-flow',
Container.get(CashFlowStatementController).router()
Container.get(CashFlowStatementController).router(),
);
router.use(
'/inventory-item-details',
Container.get(InventoryDetailsController).router()
Container.get(InventoryDetailsController).router(),
);
router.use(
'/transactions-by-reference',
Container.get(TransactionsByReferenceController).router()
Container.get(TransactionsByReferenceController).router(),
);
router.use(
'/cashflow-account-transactions',
Container.get(CashflowAccountTransactions).router()
Container.get(CashflowAccountTransactions).router(),
);
router.use(
'/project-profitability-summary',
Container.get(ProjectProfitabilityController).router()
);
router.use(
'/sales-tax-liability-summary',
Container.get(SalesTaxLiabilitySummary).router()
);
Container.get(ProjectProfitabilityController).router(),
)
return router;
}
}

View File

@@ -33,13 +33,10 @@ export default class APAgingSummaryReportController extends BaseFinancialReportC
return [
...this.sheetNumberFormatValidationSchema,
query('as_date').optional().isISO8601(),
query('aging_days_before').default(30).isInt({ max: 500 }).toInt(),
query('aging_periods').default(3).isInt({ max: 12 }).toInt(),
query('aging_days_before').optional().isNumeric().toInt(),
query('aging_periods').optional().isNumeric().toInt(),
query('vendors_ids').optional().isArray({ min: 1 }),
query('vendors_ids.*').isInt({ min: 1 }).toInt(),
query('none_zero').default(true).isBoolean().toBoolean(),
// Filtering by branches.
@@ -56,36 +53,15 @@ export default class APAgingSummaryReportController extends BaseFinancialReportC
const filter = this.matchedQueryData(req);
try {
const accept = this.accepts(req);
const acceptType = accept.types(['json', 'application/json+table']);
const { data, columns, query, meta } =
await this.APAgingSummaryService.APAgingSummary(tenantId, filter);
switch (acceptType) {
case 'application/json+table':
const table = await this.APAgingSummaryService.APAgingSummaryTable(
tenantId,
filter
);
return res.status(200).send({
table: {
rows: table.rows,
columns: table.columns,
},
meta: table.meta,
query: table.query,
});
break;
default:
const { data, columns, query, meta } =
await this.APAgingSummaryService.APAgingSummary(tenantId, filter);
return res.status(200).send({
data: this.transfromToResponse(data),
columns: this.transfromToResponse(columns),
query: this.transfromToResponse(query),
meta: this.transfromToResponse(meta),
});
break;
}
return res.status(200).send({
data: this.transfromToResponse(data),
columns: this.transfromToResponse(columns),
query: this.transfromToResponse(query),
meta: this.transfromToResponse(meta),
});
} catch (error) {
next(error);
}

View File

@@ -36,8 +36,8 @@ export default class ARAgingSummaryReportController extends BaseFinancialReportC
query('as_date').optional().isISO8601(),
query('aging_days_before').default(30).isInt({ max: 500 }).toInt(),
query('aging_periods').default(3).isInt({ max: 12 }).toInt(),
query('aging_days_before').optional().isInt({ max: 500 }).toInt(),
query('aging_periods').optional().isInt({ max: 12 }).toInt(),
query('customers_ids').optional().isArray({ min: 1 }),
query('customers_ids.*').isInt({ min: 1 }).toInt(),
@@ -58,36 +58,15 @@ export default class ARAgingSummaryReportController extends BaseFinancialReportC
const filter = this.matchedQueryData(req);
try {
const accept = this.accepts(req);
const acceptType = accept.types(['json', 'application/json+table']);
const { data, columns, query, meta } =
await this.ARAgingSummaryService.ARAgingSummary(tenantId, filter);
switch (acceptType) {
case 'application/json+table':
const table = await this.ARAgingSummaryService.ARAgingSummaryTable(
tenantId,
filter
);
return res.status(200).send({
table: {
rows: table.rows,
columns: table.columns,
},
meta: table.meta,
query: table.query,
});
break;
default:
const { data, columns, query, meta } =
await this.ARAgingSummaryService.ARAgingSummary(tenantId, filter);
return res.status(200).send({
data: this.transfromToResponse(data),
columns: this.transfromToResponse(columns),
query: this.transfromToResponse(query),
meta: this.transfromToResponse(meta),
});
break;
}
return res.status(200).send({
data: this.transfromToResponse(data),
columns: this.transfromToResponse(columns),
query: this.transfromToResponse(query),
meta: this.transfromToResponse(meta),
});
} catch (error) {
console.log(error);
}

View File

@@ -41,7 +41,7 @@ export default class BalanceSheetStatementController extends BaseFinancialReport
get balanceSheetValidationSchema(): ValidationChain[] {
return [
...this.sheetNumberFormatValidationSchema,
query('accounting_method').optional().isIn(['cash', 'accrual']),
query('accounting_method').optional().isIn(['cash', 'accural']),
query('from_date').optional(),
query('to_date').optional(),

View File

@@ -67,7 +67,6 @@ export default class GeneralLedgerReportController extends BaseFinancialReportCo
try {
const { data, query, meta } =
await this.generalLedgetService.generalLedger(tenantId, filter);
return res.status(200).send({
meta: this.transfromToResponse(meta),
data: this.transfromToResponse(data),

View File

@@ -1,90 +0,0 @@
import { Router, Request, Response, NextFunction } from 'express';
import { query } from 'express-validator';
import { Inject } from 'typedi';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import BaseFinancialReportController from '../BaseFinancialReportController';
import { AbilitySubject, ReportsAction } from '@/interfaces';
import CheckPolicies from '@/api/middleware/CheckPolicies';
import { SalesTaxLiabilitySummaryService } from '@/services/FinancialStatements/SalesTaxLiabilitySummary/SalesTaxLiabilitySummaryService';
export default class SalesTaxLiabilitySummary extends BaseFinancialReportController {
@Inject()
private salesTaxLiabilitySummaryService: SalesTaxLiabilitySummaryService;
/**
* Router constructor.
*/
router() {
const router = Router();
router.get(
'/',
CheckPolicies(
ReportsAction.READ_SALES_TAX_LIABILITY_SUMMARY,
AbilitySubject.Report
),
this.validationSchema,
asyncMiddleware(this.salesTaxLiabilitySummary.bind(this))
);
return router;
}
/**
* Validation schema.
*/
get validationSchema() {
return [
query('from_date').optional().isISO8601(),
query('to_date').optional().isISO8601(),
];
}
/*
* Retrieves the sales tax liability summary.
* @param {Request} req -
* @param {Response} res -
* @param {NextFunction} next -
*/
async salesTaxLiabilitySummary(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const filter = this.matchedQueryData(req);
try {
const accept = this.accepts(req);
const acceptType = accept.types(['json', 'application/json+table']);
switch (acceptType) {
case 'application/json+table':
const salesTaxLiabilityTable =
await this.salesTaxLiabilitySummaryService.salesTaxLiabilitySummaryTable(
tenantId,
filter
);
return res.status(200).send({
table: salesTaxLiabilityTable.table,
query: salesTaxLiabilityTable.query,
meta: salesTaxLiabilityTable.meta,
});
case 'json':
default:
const salesTaxLiability =
await this.salesTaxLiabilitySummaryService.salesTaxLiability(
tenantId,
filter
);
return res.status(200).send({
data: salesTaxLiability.data,
query: salesTaxLiability.query,
meta: salesTaxLiability.meta,
});
}
} catch (error) {
next(error);
}
}
}

View File

@@ -387,7 +387,7 @@ export default class ManualJournalsController extends BaseController {
errors: [{ type: 'CREDIT.DEBIT.NOT.EQUALS', code: 300 }],
});
}
if (error.errorType === 'accounts_ids_not_found') {
if (error.errorType === 'acccounts_ids_not_found') {
return res.boom.badRequest(
'Journal entries some of accounts ids not exists.',
{ errors: [{ type: 'ACCOUNTS.IDS.NOT.FOUND', code: 400 }] }

View File

@@ -58,7 +58,7 @@ export default class OrganizationController extends BaseController {
private get organizationValidationSchema(): ValidationChain[] {
return [
check('name').exists().trim(),
check('industry').optional({ nullable: true }).isString().trim().escape(),
check('industry').optional().isString(),
check('location').exists().isString().isISO31661Alpha2(),
check('base_currency').exists().isISO4217(),
check('timezone').exists().isIn(moment.tz.names()),

View File

@@ -1,27 +1,26 @@
import { Service, Inject } from 'typedi';
import { Router, Request, Response, NextFunction } from 'express';
import { check, param, query } from 'express-validator';
import {
AbilitySubject,
BillAction,
IBillDTO,
IBillEditDTO,
} from '@/interfaces';
import { Service, Inject } from 'typedi';
import { AbilitySubject, BillAction, IBillDTO, IBillEditDTO } from '@/interfaces';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import BillsService from '@/services/Purchases/Bills';
import BaseController from '@/api/controllers/BaseController';
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
import { ServiceError } from '@/exceptions';
import CheckPolicies from '@/api/middleware/CheckPolicies';
import { BillsApplication } from '@/services/Purchases/Bills/BillsApplication';
import BillPaymentsService from '@/services/Purchases/BillPaymentsService';
@Service()
export default class BillsController extends BaseController {
@Inject()
private billsApplication: BillsApplication;
private billsService: BillsService;
@Inject()
private dynamicListService: DynamicListingService;
@Inject()
private billPayments: BillPaymentsService;
/**
* Router constructor.
*/
@@ -98,7 +97,7 @@ export default class BillsController extends BaseController {
/**
* Common validation schema.
*/
private get billValidationSchema() {
get billValidationSchema() {
return [
check('bill_number').exists().trim().escape(),
check('reference_no').optional().trim().escape(),
@@ -143,7 +142,7 @@ export default class BillsController extends BaseController {
/**
* Common validation schema.
*/
private get billEditValidationSchema() {
get billEditValidationSchema() {
return [
check('bill_number').optional().trim().escape(),
check('reference_no').optional().trim().escape(),
@@ -185,14 +184,14 @@ export default class BillsController extends BaseController {
/**
* Bill validation schema.
*/
private get specificBillValidationSchema() {
get specificBillValidationSchema() {
return [param('id').exists().isNumeric().toInt()];
}
/**
* Bills list validation schema.
*/
private get billsListingValidationSchema() {
get billsListingValidationSchema() {
return [
query('view_slug').optional().isString().trim(),
query('stringified_filter_roles').optional().isJSON(),
@@ -204,7 +203,7 @@ export default class BillsController extends BaseController {
];
}
private get dueBillsListingValidationSchema() {
get dueBillsListingValidationSchema() {
return [
query('vendor_id').optional().trim().escape(),
query('payment_made_id').optional().trim().escape(),
@@ -217,16 +216,17 @@ export default class BillsController extends BaseController {
* @param {Response} res
* @param {Function} next
*/
private async newBill(req: Request, res: Response, next: NextFunction) {
async newBill(req: Request, res: Response, next: NextFunction) {
const { tenantId, user } = req;
const billDTO: IBillDTO = this.matchedBodyData(req);
try {
const storedBill = await this.billsApplication.createBill(
const storedBill = await this.billsService.createBill(
tenantId,
billDTO,
user
);
return res.status(200).send({
id: storedBill.id,
message: 'The bill has been created successfully.',
@@ -241,13 +241,13 @@ export default class BillsController extends BaseController {
* @param {Request} req
* @param {Response} res
*/
private async editBill(req: Request, res: Response, next: NextFunction) {
async editBill(req: Request, res: Response, next: NextFunction) {
const { id: billId } = req.params;
const { tenantId, user } = req;
const billDTO: IBillEditDTO = this.matchedBodyData(req);
try {
await this.billsApplication.editBill(tenantId, billId, billDTO, user);
await this.billsService.editBill(tenantId, billId, billDTO, user);
return res.status(200).send({
id: billId,
@@ -263,12 +263,12 @@ export default class BillsController extends BaseController {
* @param {Request} req -
* @param {Response} res -
*/
private async openBill(req: Request, res: Response, next: NextFunction) {
async openBill(req: Request, res: Response, next: NextFunction) {
const { id: billId } = req.params;
const { tenantId } = req;
try {
await this.billsApplication.openBill(tenantId, billId);
await this.billsService.openBill(tenantId, billId);
return res.status(200).send({
id: billId,
@@ -285,12 +285,12 @@ export default class BillsController extends BaseController {
* @param {Response} res
* @return {Response}
*/
private async getBill(req: Request, res: Response, next: NextFunction) {
async getBill(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { id: billId } = req.params;
try {
const bill = await this.billsApplication.getBill(tenantId, billId);
const bill = await this.billsService.getBill(tenantId, billId);
return res.status(200).send(this.transfromToResponse({ bill }));
} catch (error) {
@@ -304,12 +304,12 @@ export default class BillsController extends BaseController {
* @param {Response} res -
* @return {Response}
*/
private async deleteBill(req: Request, res: Response, next: NextFunction) {
async deleteBill(req: Request, res: Response, next: NextFunction) {
const billId = req.params.id;
const { tenantId } = req;
try {
await this.billsApplication.deleteBill(tenantId, billId);
await this.billsService.deleteBill(tenantId, billId);
return res.status(200).send({
id: billId,
@@ -326,7 +326,7 @@ export default class BillsController extends BaseController {
* @param {Response} res -
* @return {Response}
*/
private async billsList(req: Request, res: Response, next: NextFunction) {
public async billsList(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const filter = {
page: 1,
@@ -338,7 +338,7 @@ export default class BillsController extends BaseController {
try {
const { bills, pagination, filterMeta } =
await this.billsApplication.getBills(tenantId, filter);
await this.billsService.getBills(tenantId, filter);
return res.status(200).send({
bills: this.transfromToResponse(bills),
@@ -356,13 +356,12 @@ export default class BillsController extends BaseController {
* @param {Response} res
* @param {NextFunction} next
*/
private async getDueBills(req: Request, res: Response, next: NextFunction) {
public async getDueBills(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { vendorId } = this.matchedQueryData(req);
try {
const bills = await this.billsApplication.getDueBills(tenantId, vendorId);
const bills = await this.billsService.getDueBills(tenantId, vendorId);
return res.status(200).send({ bills });
} catch (error) {
next(error);
@@ -375,7 +374,7 @@ export default class BillsController extends BaseController {
* @param {Response} res
* @param {NextFunction} next
*/
private getBillPaymentsTransactions = async (
public getBillPaymentsTransactions = async (
req: Request,
res: Response,
next: NextFunction
@@ -384,7 +383,7 @@ export default class BillsController extends BaseController {
const { id: billId } = req.params;
try {
const billPayments = await this.billsApplication.getBillPayments(
const billPayments = await this.billPayments.getBillPayments(
tenantId,
billId
);

View File

@@ -4,7 +4,7 @@ import { check, param, query, ValidationChain } from 'express-validator';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import { ServiceError } from '@/exceptions';
import BaseController from '@/api/controllers/BaseController';
import { BillPaymentsApplication } from '@/services/Purchases/BillPayments/BillPaymentsApplication';
import BillPaymentsService from '@/services/Purchases/BillPayments/BillPayments';
import BillPaymentsPages from '@/services/Purchases/BillPayments/BillPaymentsPages';
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
import CheckPolicies from '@/api/middleware/CheckPolicies';
@@ -17,18 +17,18 @@ import { AbilitySubject, IPaymentMadeAction } from '@/interfaces';
@Service()
export default class BillsPayments extends BaseController {
@Inject()
private billPaymentsApplication: BillPaymentsApplication;
billPaymentService: BillPaymentsService;
@Inject()
private dynamicListService: DynamicListingService;
dynamicListService: DynamicListingService;
@Inject()
private billPaymentsPages: BillPaymentsPages;
billPaymentsPages: BillPaymentsPages;
/**
* Router constructor.
*/
public router() {
router() {
const router = Router();
router.post(
@@ -106,7 +106,7 @@ export default class BillsPayments extends BaseController {
* Bill payments schema validation.
* @return {ValidationChain[]}
*/
private get billPaymentSchemaValidation(): ValidationChain[] {
get billPaymentSchemaValidation(): ValidationChain[] {
return [
check('vendor_id').exists().isNumeric().toInt(),
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
@@ -129,7 +129,7 @@ export default class BillsPayments extends BaseController {
* Specific bill payment schema validation.
* @returns {ValidationChain[]}
*/
private get specificBillPaymentValidateSchema(): ValidationChain[] {
get specificBillPaymentValidateSchema(): ValidationChain[] {
return [param('id').exists().isNumeric().toInt()];
}
@@ -137,7 +137,7 @@ export default class BillsPayments extends BaseController {
* Bills payment list validation schema.
* @returns {ValidationChain[]}
*/
private get listingValidationSchema(): ValidationChain[] {
get listingValidationSchema(): ValidationChain[] {
return [
query('custom_view_id').optional().isNumeric().toInt(),
query('stringified_filter_roles').optional().isJSON(),
@@ -154,7 +154,7 @@ export default class BillsPayments extends BaseController {
* @param {Request} req -
* @param {Response} res -
*/
private async getBillPaymentNewPageEntries(req: Request, res: Response) {
async getBillPaymentNewPageEntries(req: Request, res: Response) {
const { tenantId } = req;
const { vendorId } = this.matchedQueryData(req);
@@ -174,7 +174,7 @@ export default class BillsPayments extends BaseController {
* @param {Request} req
* @param {Response} res
*/
private async getBillPaymentEditPage(
async getBillPaymentEditPage(
req: Request,
res: Response,
next: NextFunction
@@ -205,19 +205,16 @@ export default class BillsPayments extends BaseController {
* @param {Response} res
* @param {Response} res
*/
private async createBillPayment(
req: Request,
res: Response,
next: NextFunction
) {
async createBillPayment(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const billPaymentDTO = this.matchedBodyData(req);
try {
const billPayment = await this.billPaymentsApplication.createBillPayment(
const billPayment = await this.billPaymentService.createBillPayment(
tenantId,
billPaymentDTO
);
return res.status(200).send({
id: billPayment.id,
message: 'Payment made has been created successfully.',
@@ -232,17 +229,13 @@ export default class BillsPayments extends BaseController {
* @param {Request} req
* @param {Response} res
*/
private async editBillPayment(
req: Request,
res: Response,
next: NextFunction
) {
async editBillPayment(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const billPaymentDTO = this.matchedBodyData(req);
const { id: billPaymentId } = req.params;
try {
const paymentMade = await this.billPaymentsApplication.editBillPayment(
const paymentMade = await this.billPaymentService.editBillPayment(
tenantId,
billPaymentId,
billPaymentDTO
@@ -263,19 +256,12 @@ export default class BillsPayments extends BaseController {
* @param {Response} res -
* @return {Response} res -
*/
private async deleteBillPayment(
req: Request,
res: Response,
next: NextFunction
) {
async deleteBillPayment(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { id: billPaymentId } = req.params;
try {
await this.billPaymentsApplication.deleteBillPayment(
tenantId,
billPaymentId
);
await this.billPaymentService.deleteBillPayment(tenantId, billPaymentId);
return res.status(200).send({
id: billPaymentId,
@@ -291,19 +277,16 @@ export default class BillsPayments extends BaseController {
* @param {Request} req
* @param {Response} res
*/
private async getBillPayment(
req: Request,
res: Response,
next: NextFunction
) {
async getBillPayment(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { id: billPaymentId } = req.params;
try {
const billPayment = await this.billPaymentsApplication.getBillPayment(
const billPayment = await this.billPaymentService.getBillPayment(
tenantId,
billPaymentId
);
return res.status(200).send({
bill_payment: this.transfromToResponse(billPayment),
});
@@ -318,16 +301,12 @@ export default class BillsPayments extends BaseController {
* @param {Response} res
* @param {NextFunction} next
*/
private async getPaymentBills(
req: Request,
res: Response,
next: NextFunction
) {
async getPaymentBills(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { id: billPaymentId } = req.params;
try {
const bills = await this.billPaymentsApplication.getPaymentBills(
const bills = await this.billPaymentService.getPaymentBills(
tenantId,
billPaymentId
);
@@ -343,11 +322,7 @@ export default class BillsPayments extends BaseController {
* @param {Response} res -
* @return {Response}
*/
private async getBillsPayments(
req: Request,
res: Response,
next: NextFunction
) {
async getBillsPayments(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const billPaymentsFilter = {
page: 1,
@@ -360,7 +335,7 @@ export default class BillsPayments extends BaseController {
try {
const { billPayments, pagination, filterMeta } =
await this.billPaymentsApplication.getBillPayments(
await this.billPaymentService.listBillPayments(
tenantId,
billPaymentsFilter
);
@@ -382,7 +357,7 @@ export default class BillsPayments extends BaseController {
* @param {Response} res
* @param {NextFunction} next
*/
private handleServiceError(
handleServiceError(
error: Error,
req: Request,
res: Response,

View File

@@ -1,29 +1,42 @@
import { Inject, Service } from 'typedi';
import { Router, Request, Response, NextFunction } from 'express';
import { check, param, query, ValidationChain } from 'express-validator';
import { Inject, Service } from 'typedi';
import {
AbilitySubject,
IPaymentReceiveDTO,
PaymentReceiveAction,
SaleInvoiceAction,
} from '@/interfaces';
import BaseController from '@/api/controllers/BaseController';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import PaymentReceiveService from '@/services/Sales/PaymentReceives/PaymentsReceives';
import PaymentReceivesPages from '@/services/Sales/PaymentReceives/PaymentReceivesPages';
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
import { PaymentReceivesApplication } from '@/services/Sales/PaymentReceives/PaymentReceivesApplication';
import CheckPolicies from '@/api/middleware/CheckPolicies';
import { ServiceError } from '@/exceptions';
import PaymentReceiveNotifyBySms from '@/services/Sales/PaymentReceives/PaymentReceiveSmsNotify';
import CheckPolicies from '@/api/middleware/CheckPolicies';
import GetPaymentReceivePdf from '@/services/Sales/PaymentReceives/GetPaymentReeceivePdf';
/**
* Payments receives controller.
* @service
*/
@Service()
export default class PaymentReceivesController extends BaseController {
@Inject()
private paymentReceiveApplication: PaymentReceivesApplication;
paymentReceiveService: PaymentReceiveService;
@Inject()
private PaymentReceivesPages: PaymentReceivesPages;
PaymentReceivesPages: PaymentReceivesPages;
@Inject()
private dynamicListService: DynamicListingService;
dynamicListService: DynamicListingService;
@Inject()
paymentReceiveSmsNotify: PaymentReceiveNotifyBySms;
@Inject()
paymentReceivePdf: GetPaymentReceivePdf;
/**
* Router constructor.
@@ -124,7 +137,7 @@ export default class PaymentReceivesController extends BaseController {
* Payment receive schema.
* @return {Array}
*/
private get paymentReceiveSchema(): ValidationChain[] {
get paymentReceiveSchema(): ValidationChain[] {
return [
check('customer_id').exists().isNumeric().toInt(),
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
@@ -149,7 +162,7 @@ export default class PaymentReceivesController extends BaseController {
/**
* Payment receive list validation schema.
*/
private get validatePaymentReceiveList(): ValidationChain[] {
get validatePaymentReceiveList(): ValidationChain[] {
return [
query('stringified_filter_roles').optional().isJSON(),
@@ -168,7 +181,7 @@ export default class PaymentReceivesController extends BaseController {
/**
* Validate payment receive parameters.
*/
private get paymentReceiveValidation() {
get paymentReceiveValidation() {
return [param('id').exists().isNumeric().toInt()];
}
@@ -176,14 +189,14 @@ export default class PaymentReceivesController extends BaseController {
* New payment receive validation schema.
* @return {Array}
*/
private get newPaymentReceiveValidation() {
get newPaymentReceiveValidation() {
return [...this.paymentReceiveSchema];
}
/**
* Edit payment receive validation.
*/
private get editPaymentReceiveValidation() {
get editPaymentReceiveValidation() {
return [
param('id').exists().isNumeric().toInt(),
...this.paymentReceiveSchema,
@@ -196,17 +209,13 @@ export default class PaymentReceivesController extends BaseController {
* @param {Response} res
* @return {Response}
*/
private async newPaymentReceive(
req: Request,
res: Response,
next: NextFunction
) {
async newPaymentReceive(req: Request, res: Response, next: NextFunction) {
const { tenantId, user } = req;
const paymentReceive: IPaymentReceiveDTO = this.matchedBodyData(req);
try {
const storedPaymentReceive =
await this.paymentReceiveApplication.createPaymentReceive(
await this.paymentReceiveService.createPaymentReceive(
tenantId,
paymentReceive,
user
@@ -226,18 +235,14 @@ export default class PaymentReceivesController extends BaseController {
* @param {Response} res
* @return {Response}
*/
private async editPaymentReceive(
req: Request,
res: Response,
next: NextFunction
) {
async editPaymentReceive(req: Request, res: Response, next: NextFunction) {
const { tenantId, user } = req;
const { id: paymentReceiveId } = req.params;
const paymentReceive: IPaymentReceiveDTO = this.matchedBodyData(req);
try {
await this.paymentReceiveApplication.editPaymentReceive(
await this.paymentReceiveService.editPaymentReceive(
tenantId,
paymentReceiveId,
paymentReceive,
@@ -257,20 +262,17 @@ export default class PaymentReceivesController extends BaseController {
* @param {Request} req
* @param {Response} res
*/
private async deletePaymentReceive(
req: Request,
res: Response,
next: NextFunction
) {
async deletePaymentReceive(req: Request, res: Response, next: NextFunction) {
const { tenantId, user } = req;
const { id: paymentReceiveId } = req.params;
try {
await this.paymentReceiveApplication.deletePaymentReceive(
await this.paymentReceiveService.deletePaymentReceive(
tenantId,
paymentReceiveId,
user
);
return res.status(200).send({
id: paymentReceiveId,
message: 'The payment receive has been deleted successfully',
@@ -286,7 +288,7 @@ export default class PaymentReceivesController extends BaseController {
* @param {Response} res
* @param {NextFunction} next
*/
private async getPaymentReceiveInvoices(
async getPaymentReceiveInvoices(
req: Request,
res: Response,
next: NextFunction
@@ -296,7 +298,7 @@ export default class PaymentReceivesController extends BaseController {
try {
const saleInvoices =
await this.paymentReceiveApplication.getPaymentReceiveInvoices(
await this.paymentReceiveService.getPaymentReceiveInvoices(
tenantId,
paymentReceiveId
);
@@ -313,11 +315,7 @@ export default class PaymentReceivesController extends BaseController {
* @param {Response} res
* @return {Response}
*/
private async getPaymentReceiveList(
req: Request,
res: Response,
next: NextFunction
) {
async getPaymentReceiveList(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const filter = {
sortOrder: 'desc',
@@ -329,10 +327,7 @@ export default class PaymentReceivesController extends BaseController {
try {
const { paymentReceives, pagination, filterMeta } =
await this.paymentReceiveApplication.getPaymentReceives(
tenantId,
filter
);
await this.paymentReceiveService.listPaymentReceives(tenantId, filter);
return res.status(200).send({
payment_receives: this.transfromToResponse(paymentReceives),
@@ -349,7 +344,7 @@ export default class PaymentReceivesController extends BaseController {
* @param {Request} req - Request.
* @param {Response} res - Response.
*/
private async getPaymentReceiveNewPageEntries(
async getPaymentReceiveNewPageEntries(
req: Request,
res: Response,
next: NextFunction
@@ -372,11 +367,11 @@ export default class PaymentReceivesController extends BaseController {
/**
* Retrieve the given payment receive details.
* @async
* @asycn
* @param {Request} req -
* @param {Response} res -
*/
private async getPaymentReceiveEditPage(
async getPaymentReceiveEditPage(
req: Request,
res: Response,
next: NextFunction
@@ -407,20 +402,15 @@ export default class PaymentReceivesController extends BaseController {
* @param {Response} res
* @param {NextFunction} next
*/
private async getPaymentReceive(
req: Request,
res: Response,
next: NextFunction
) {
async getPaymentReceive(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { id: paymentReceiveId } = req.params;
try {
const paymentReceive =
await this.paymentReceiveApplication.getPaymentReceive(
tenantId,
paymentReceiveId
);
const paymentReceive = await this.paymentReceiveService.getPaymentReceive(
tenantId,
paymentReceiveId
);
const ACCEPT_TYPE = {
APPLICATION_PDF: 'application/pdf',
@@ -433,11 +423,10 @@ export default class PaymentReceivesController extends BaseController {
});
},
[ACCEPT_TYPE.APPLICATION_PDF]: async () => {
const pdfContent =
await this.paymentReceiveApplication.getPaymentReceivePdf(
tenantId,
paymentReceive
);
const pdfContent = await this.paymentReceivePdf.getPaymentReceivePdf(
tenantId,
paymentReceive
);
res.set({
'Content-Type': 'application/pdf',
'Content-Length': pdfContent.length,
@@ -465,11 +454,10 @@ export default class PaymentReceivesController extends BaseController {
const { id: paymentReceiveId } = req.params;
try {
const paymentReceive =
await this.paymentReceiveApplication.notifyPaymentBySms(
tenantId,
paymentReceiveId
);
const paymentReceive = await this.paymentReceiveSmsNotify.notifyBySms(
tenantId,
paymentReceiveId
);
return res.status(200).send({
id: paymentReceive.id,
message: 'The payment notification has been sent successfully.',
@@ -494,11 +482,10 @@ export default class PaymentReceivesController extends BaseController {
const { id: paymentReceiveId } = req.params;
try {
const smsDetails =
await this.paymentReceiveApplication.getPaymentSmsDetails(
tenantId,
paymentReceiveId
);
const smsDetails = await this.paymentReceiveSmsNotify.smsDetails(
tenantId,
paymentReceiveId
);
return res.status(200).send({
data: smsDetails,
});

View File

@@ -1,17 +1,20 @@
import { Router, Request, Response, NextFunction } from 'express';
import { check, param, query } from 'express-validator';
import { check, param, query, matchedData } from 'express-validator';
import { Inject, Service } from 'typedi';
import {
AbilitySubject,
ISaleEstimateDTO,
SaleEstimateAction,
SaleInvoiceAction,
} from '@/interfaces';
import BaseController from '@/api/controllers/BaseController';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import SaleEstimateService from '@/services/Sales/SalesEstimate';
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
import { ServiceError } from '@/exceptions';
import SaleEstimatesPdfService from '@/services/Sales/Estimates/SaleEstimatesPdf';
import SaleEstimateNotifyBySms from '@/services/Sales/Estimates/SaleEstimateSmsNotify';
import CheckPolicies from '@/api/middleware/CheckPolicies';
import { SaleEstimatesApplication } from '@/services/Sales/Estimates/SaleEstimatesApplication';
const ACCEPT_TYPE = {
APPLICATION_PDF: 'application/pdf',
@@ -20,15 +23,21 @@ const ACCEPT_TYPE = {
@Service()
export default class SalesEstimatesController extends BaseController {
@Inject()
private saleEstimatesApplication: SaleEstimatesApplication;
saleEstimateService: SaleEstimateService;
@Inject()
private dynamicListService: DynamicListingService;
dynamicListService: DynamicListingService;
@Inject()
saleEstimatesPdf: SaleEstimatesPdfService;
@Inject()
saleEstimateNotifySms: SaleEstimateNotifyBySms;
/**
* Router constructor.
*/
public router() {
router() {
const router = Router();
router.post(
@@ -127,7 +136,7 @@ export default class SalesEstimatesController extends BaseController {
/**
* Estimate validation schema.
*/
private get estimateValidationSchema() {
get estimateValidationSchema() {
return [
check('customer_id').exists().isNumeric().toInt(),
check('estimate_date').exists().isISO8601().toDate(),
@@ -168,14 +177,14 @@ export default class SalesEstimatesController extends BaseController {
/**
* Specific sale estimate validation schema.
*/
private get validateSpecificEstimateSchema() {
get validateSpecificEstimateSchema() {
return [param('id').exists().isNumeric().toInt()];
}
/**
* Sales estimates list validation schema.
*/
private get validateEstimateListSchema() {
get validateEstimateListSchema() {
return [
query('view_slug').optional().isString().trim(),
query('stringified_filter_roles').optional().isJSON(),
@@ -193,16 +202,15 @@ export default class SalesEstimatesController extends BaseController {
* @param {Response} res -
* @return {Response} res -
*/
private async newEstimate(req: Request, res: Response, next: NextFunction) {
async newEstimate(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const estimateDTO: ISaleEstimateDTO = this.matchedBodyData(req);
try {
const storedEstimate =
await this.saleEstimatesApplication.createSaleEstimate(
tenantId,
estimateDTO
);
const storedEstimate = await this.saleEstimateService.createEstimate(
tenantId,
estimateDTO
);
return res.status(200).send({
id: storedEstimate.id,
@@ -218,18 +226,19 @@ export default class SalesEstimatesController extends BaseController {
* @param {Request} req
* @param {Response} res
*/
private async editEstimate(req: Request, res: Response, next: NextFunction) {
async editEstimate(req: Request, res: Response, next: NextFunction) {
const { id: estimateId } = req.params;
const { tenantId } = req;
const estimateDTO: ISaleEstimateDTO = this.matchedBodyData(req);
try {
// Update estimate with associated estimate entries.
await this.saleEstimatesApplication.editSaleEstimate(
await this.saleEstimateService.editEstimate(
tenantId,
estimateId,
estimateDTO
);
return res.status(200).send({
id: estimateId,
message: 'The sale estimate has been created successfully.',
@@ -244,19 +253,13 @@ export default class SalesEstimatesController extends BaseController {
* @param {Request} req
* @param {Response} res
*/
private async deleteEstimate(
req: Request,
res: Response,
next: NextFunction
) {
async deleteEstimate(req: Request, res: Response, next: NextFunction) {
const { id: estimateId } = req.params;
const { tenantId } = req;
try {
await this.saleEstimatesApplication.deleteSaleEstimate(
tenantId,
estimateId
);
await this.saleEstimateService.deleteEstimate(tenantId, estimateId);
return res.status(200).send({
id: estimateId,
message: 'The sale estimate has been deleted successfully.',
@@ -271,19 +274,13 @@ export default class SalesEstimatesController extends BaseController {
* @param {Request} req
* @param {Response} res
*/
private async deliverSaleEstimate(
req: Request,
res: Response,
next: NextFunction
) {
async deliverSaleEstimate(req: Request, res: Response, next: NextFunction) {
const { id: estimateId } = req.params;
const { tenantId } = req;
try {
await this.saleEstimatesApplication.deliverSaleEstimate(
tenantId,
estimateId
);
await this.saleEstimateService.deliverSaleEstimate(tenantId, estimateId);
return res.status(200).send({
id: estimateId,
message: 'The sale estimate has been delivered successfully.',
@@ -299,19 +296,12 @@ export default class SalesEstimatesController extends BaseController {
* @param {Response} res
* @param {NextFunction} next
*/
private async approveSaleEstimate(
req: Request,
res: Response,
next: NextFunction
) {
async approveSaleEstimate(req: Request, res: Response, next: NextFunction) {
const { id: estimateId } = req.params;
const { tenantId } = req;
try {
await this.saleEstimatesApplication.approveSaleEstimate(
tenantId,
estimateId
);
await this.saleEstimateService.approveSaleEstimate(tenantId, estimateId);
return res.status(200).send({
id: estimateId,
@@ -328,19 +318,12 @@ export default class SalesEstimatesController extends BaseController {
* @param {Response} res
* @param {NextFunction} next
*/
private async rejectSaleEstimate(
req: Request,
res: Response,
next: NextFunction
) {
async rejectSaleEstimate(req: Request, res: Response, next: NextFunction) {
const { id: estimateId } = req.params;
const { tenantId } = req;
try {
await this.saleEstimatesApplication.rejectSaleEstimate(
tenantId,
estimateId
);
await this.saleEstimateService.rejectSaleEstimate(tenantId, estimateId);
return res.status(200).send({
id: estimateId,
@@ -357,12 +340,12 @@ export default class SalesEstimatesController extends BaseController {
* @param {Response} res
* @param {NextFunction} next
*/
private async getEstimate(req: Request, res: Response, next: NextFunction) {
async getEstimate(req: Request, res: Response, next: NextFunction) {
const { id: estimateId } = req.params;
const { tenantId } = req;
try {
const estimate = await this.saleEstimatesApplication.getSaleEstimate(
const estimate = await this.saleEstimateService.getEstimate(
tenantId,
estimateId
);
@@ -374,11 +357,10 @@ export default class SalesEstimatesController extends BaseController {
},
// PDF content type.
[ACCEPT_TYPE.APPLICATION_PDF]: async () => {
const pdfContent =
await this.saleEstimatesApplication.getSaleEstimatePdf(
tenantId,
estimate
);
const pdfContent = await this.saleEstimatesPdf.saleEstimatePdf(
tenantId,
estimate
);
res.set({
'Content-Type': 'application/pdf',
'Content-Length': pdfContent.length,
@@ -396,7 +378,7 @@ export default class SalesEstimatesController extends BaseController {
* @param {Request} req
* @param {Response} res
*/
private async getEstimates(req: Request, res: Response, next: NextFunction) {
async getEstimates(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const filter = {
sortOrder: 'desc',
@@ -408,7 +390,7 @@ export default class SalesEstimatesController extends BaseController {
try {
const { salesEstimates, pagination, filterMeta } =
await this.saleEstimatesApplication.getSaleEstimates(tenantId, filter);
await this.saleEstimateService.estimatesList(tenantId, filter);
res.format({
[ACCEPT_TYPE.APPLICATION_JSON]: () => {
@@ -426,7 +408,7 @@ export default class SalesEstimatesController extends BaseController {
}
}
private saleEstimateNotifyBySms = async (
public saleEstimateNotifyBySms = async (
req: Request,
res: Response,
next: NextFunction
@@ -435,11 +417,10 @@ export default class SalesEstimatesController extends BaseController {
const { id: estimateId } = req.params;
try {
const saleEstimate =
await this.saleEstimatesApplication.notifySaleEstimateBySms(
tenantId,
estimateId
);
const saleEstimate = await this.saleEstimateNotifySms.notifyBySms(
tenantId,
estimateId
);
return res.status(200).send({
id: saleEstimate.id,
message:
@@ -456,7 +437,7 @@ export default class SalesEstimatesController extends BaseController {
* @param {Response} res
* @param {NextFunction} next
*/
private saleEstimateSmsDetails = async (
public saleEstimateSmsDetails = async (
req: Request,
res: Response,
next: NextFunction
@@ -465,11 +446,10 @@ export default class SalesEstimatesController extends BaseController {
const { id: estimateId } = req.params;
try {
const estimateSmsDetails =
await this.saleEstimatesApplication.getSaleEstimateSmsDetails(
tenantId,
estimateId
);
const estimateSmsDetails = await this.saleEstimateNotifySms.smsDetails(
tenantId,
estimateId
);
return res.status(200).send({
data: estimateSmsDetails,
});

View File

@@ -3,6 +3,7 @@ import { check, param, query } from 'express-validator';
import { Service, Inject } from 'typedi';
import BaseController from '../BaseController';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import SaleInvoiceService from '@/services/Sales/SalesInvoices';
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
import { ServiceError } from '@/exceptions';
import {
@@ -11,8 +12,11 @@ import {
SaleInvoiceAction,
AbilitySubject,
} from '@/interfaces';
import SaleInvoicePdf from '@/services/Sales/SaleInvoicePdf';
import SaleInvoiceWriteoff from '@/services/Sales/SaleInvoiceWriteoff';
import SaleInvoiceNotifyBySms from '@/services/Sales/SaleInvoiceNotifyBySms';
import CheckPolicies from '@/api/middleware/CheckPolicies';
import { SaleInvoiceApplication } from '@/services/Sales/Invoices/SaleInvoicesApplication';
import InvoicePaymentsService from '@/services/Sales/Invoices/InvoicePaymentsService';
const ACCEPT_TYPE = {
APPLICATION_PDF: 'application/pdf',
@@ -21,15 +25,27 @@ const ACCEPT_TYPE = {
@Service()
export default class SaleInvoicesController extends BaseController {
@Inject()
private saleInvoiceApplication: SaleInvoiceApplication;
saleInvoiceService: SaleInvoiceService;
@Inject()
private dynamicListService: DynamicListingService;
dynamicListService: DynamicListingService;
@Inject()
saleInvoicePdf: SaleInvoicePdf;
@Inject()
saleInvoiceWriteoff: SaleInvoiceWriteoff;
@Inject()
saleInvoiceSmsNotify: SaleInvoiceNotifyBySms;
@Inject()
invoicePaymentsSerivce: InvoicePaymentsService;
/**
* Router constructor.
*/
public router() {
router() {
const router = Router();
router.post(
@@ -151,7 +167,7 @@ export default class SaleInvoicesController extends BaseController {
/**
* Sale invoice validation schema.
*/
private get saleInvoiceValidationSchema() {
get saleInvoiceValidationSchema() {
return [
check('customer_id').exists().isNumeric().toInt(),
check('invoice_date').exists().isISO8601().toDate(),
@@ -169,9 +185,8 @@ export default class SaleInvoicesController extends BaseController {
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
check('project_id').optional({ nullable: true }).isNumeric().toInt(),
check('is_inclusive_tax').optional().isBoolean().toBoolean(),
check('entries').exists().isArray({ min: 1 }),
check('entries.*.index').exists().isNumeric().toInt(),
check('entries.*.item_id').exists().isNumeric().toInt(),
check('entries.*.rate').exists().isNumeric().toFloat(),
@@ -184,15 +199,6 @@ export default class SaleInvoicesController extends BaseController {
.optional({ nullable: true })
.trim()
.escape(),
check('entries.*.tax_code')
.optional({ nullable: true })
.trim()
.escape()
.isString(),
check('entries.*.tax_rate_id')
.optional({ nullable: true })
.isNumeric()
.toInt(),
check('entries.*.warehouse_id')
.optional({ nullable: true })
.isNumeric()
@@ -221,14 +227,14 @@ export default class SaleInvoicesController extends BaseController {
/**
* Specific sale invoice validation schema.
*/
private get specificSaleInvoiceValidation() {
get specificSaleInvoiceValidation() {
return [param('id').exists().isNumeric().toInt()];
}
/**
* Sales invoices list validation schema.
*/
private get saleInvoiceListValidationSchema() {
get saleInvoiceListValidationSchema() {
return [
query('view_slug').optional({ nullable: true }).isString().trim(),
query('stringified_filter_roles').optional().isJSON(),
@@ -243,7 +249,7 @@ export default class SaleInvoicesController extends BaseController {
/**
* Due sale invoice list validation schema.
*/
private get dueSalesInvoicesListValidationSchema() {
get dueSalesInvoicesListValidationSchema() {
return [query('customer_id').optional().isNumeric().toInt()];
}
@@ -253,22 +259,17 @@ export default class SaleInvoicesController extends BaseController {
* @param {Response} res
* @param {Function} next
*/
private async newSaleInvoice(
req: Request,
res: Response,
next: NextFunction
) {
async newSaleInvoice(req: Request, res: Response, next: NextFunction) {
const { tenantId, user } = req;
const saleInvoiceDTO: ISaleInvoiceCreateDTO = this.matchedBodyData(req);
try {
// Creates a new sale invoice with associated entries.
const storedSaleInvoice =
await this.saleInvoiceApplication.createSaleInvoice(
tenantId,
saleInvoiceDTO,
user
);
const storedSaleInvoice = await this.saleInvoiceService.createSaleInvoice(
tenantId,
saleInvoiceDTO,
user
);
return res.status(200).send({
id: storedSaleInvoice.id,
message: 'The sale invoice has been created successfully.',
@@ -284,18 +285,14 @@ export default class SaleInvoicesController extends BaseController {
* @param {Response} res
* @param {Function} next
*/
private async editSaleInvoice(
req: Request,
res: Response,
next: NextFunction
) {
async editSaleInvoice(req: Request, res: Response, next: NextFunction) {
const { tenantId, user } = req;
const { id: saleInvoiceId } = req.params;
const saleInvoiceOTD: ISaleInvoiceDTO = this.matchedBodyData(req);
try {
// Update the given sale invoice details.
await this.saleInvoiceApplication.editSaleInvoice(
await this.saleInvoiceService.editSaleInvoice(
tenantId,
saleInvoiceId,
saleInvoiceOTD,
@@ -316,16 +313,12 @@ export default class SaleInvoicesController extends BaseController {
* @param {Response} res -
* @param {NextFunction} next -
*/
private async deliverSaleInvoice(
req: Request,
res: Response,
next: NextFunction
) {
async deliverSaleInvoice(req: Request, res: Response, next: NextFunction) {
const { tenantId, user } = req;
const { id: saleInvoiceId } = req.params;
try {
await this.saleInvoiceApplication.deliverSaleInvoice(
await this.saleInvoiceService.deliverSaleInvoice(
tenantId,
saleInvoiceId,
user
@@ -345,17 +338,13 @@ export default class SaleInvoicesController extends BaseController {
* @param {Response} res
* @param {Function} next
*/
private async deleteSaleInvoice(
req: Request,
res: Response,
next: NextFunction
) {
async deleteSaleInvoice(req: Request, res: Response, next: NextFunction) {
const { id: saleInvoiceId } = req.params;
const { tenantId, user } = req;
try {
// Deletes the sale invoice with associated entries and journal transaction.
await this.saleInvoiceApplication.deleteSaleInvoice(
await this.saleInvoiceService.deleteSaleInvoice(
tenantId,
saleInvoiceId,
user
@@ -375,16 +364,12 @@ export default class SaleInvoicesController extends BaseController {
* @param {Request} req - Request object.
* @param {Response} res - Response object.
*/
private async getSaleInvoice(
req: Request,
res: Response,
next: NextFunction
) {
async getSaleInvoice(req: Request, res: Response, next: NextFunction) {
const { id: saleInvoiceId } = req.params;
const { tenantId, user } = req;
try {
const saleInvoice = await this.saleInvoiceApplication.getSaleInvoice(
const saleInvoice = await this.saleInvoiceService.getSaleInvoice(
tenantId,
saleInvoiceId,
user
@@ -399,7 +384,7 @@ export default class SaleInvoicesController extends BaseController {
},
// PDF content type.
[ACCEPT_TYPE.APPLICATION_PDF]: async () => {
const pdfContent = await this.saleInvoiceApplication.saleInvoicePdf(
const pdfContent = await this.saleInvoicePdf.saleInvoicePdf(
tenantId,
saleInvoice
);
@@ -435,7 +420,7 @@ export default class SaleInvoicesController extends BaseController {
};
try {
const { salesInvoices, filterMeta, pagination } =
await this.saleInvoiceApplication.getSaleInvoices(tenantId, filter);
await this.saleInvoiceService.salesInvoicesList(tenantId, filter);
return res.status(200).send({
sales_invoices: this.transfromToResponse(salesInvoices),
@@ -463,11 +448,10 @@ export default class SaleInvoicesController extends BaseController {
const { customerId } = this.matchedQueryData(req);
try {
const salesInvoices =
await this.saleInvoiceApplication.getReceivableSaleInvoices(
tenantId,
customerId
);
const salesInvoices = await this.saleInvoiceService.getPayableInvoices(
tenantId,
customerId
);
return res.status(200).send({
sales_invoices: this.transfromToResponse(salesInvoices),
});
@@ -493,7 +477,7 @@ export default class SaleInvoicesController extends BaseController {
const writeoffDTO = this.matchedBodyData(req);
try {
const saleInvoice = await this.saleInvoiceApplication.writeOff(
const saleInvoice = await this.saleInvoiceWriteoff.writeOff(
tenantId,
invoiceId,
writeoffDTO
@@ -501,7 +485,7 @@ export default class SaleInvoicesController extends BaseController {
return res.status(200).send({
id: saleInvoice.id,
message: 'The given sale invoice has been written-off successfully.',
message: 'The given sale invoice has been writte-off successfully.',
});
} catch (error) {
next(error);
@@ -523,7 +507,7 @@ export default class SaleInvoicesController extends BaseController {
const { id: invoiceId } = req.params;
try {
const saleInvoice = await this.saleInvoiceApplication.cancelWrittenoff(
const saleInvoice = await this.saleInvoiceWriteoff.cancelWrittenoff(
tenantId,
invoiceId
);
@@ -554,12 +538,11 @@ export default class SaleInvoicesController extends BaseController {
const invoiceNotifySmsDTO = this.matchedBodyData(req);
try {
const saleInvoice =
await this.saleInvoiceApplication.notifySaleInvoiceBySms(
tenantId,
invoiceId,
invoiceNotifySmsDTO.notificationKey
);
const saleInvoice = await this.saleInvoiceSmsNotify.notifyBySms(
tenantId,
invoiceId,
invoiceNotifySmsDTO.notificationKey
);
return res.status(200).send({
id: saleInvoice.id,
message:
@@ -586,12 +569,11 @@ export default class SaleInvoicesController extends BaseController {
const smsDetailsDTO = this.matchedQueryData(req);
try {
const invoiceSmsDetails =
await this.saleInvoiceApplication.getSaleInvoiceSmsDetails(
tenantId,
invoiceId,
smsDetailsDTO
);
const invoiceSmsDetails = await this.saleInvoiceSmsNotify.smsDetails(
tenantId,
invoiceId,
smsDetailsDTO
);
return res.status(200).send({
data: invoiceSmsDetails,
});
@@ -617,7 +599,7 @@ export default class SaleInvoicesController extends BaseController {
try {
const invoicePayments =
await this.saleInvoiceApplication.getInvoicePayments(
await this.invoicePaymentsSerivce.getInvoicePayments(
tenantId,
invoiceId
);
@@ -766,16 +748,6 @@ export default class SaleInvoicesController 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: 5000 }],
});
}
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: 5100 }],
});
}
}
next(error);
}

View File

@@ -2,26 +2,34 @@ import { Router, Request, Response, NextFunction } from 'express';
import { check, param, query } from 'express-validator';
import { Inject, Service } from 'typedi';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import SaleReceiptService from '@/services/Sales/SalesReceipts';
import SaleReceiptsPdfService from '@/services/Sales/Receipts/SaleReceiptsPdfService';
import BaseController from '../BaseController';
import { ISaleReceiptDTO } from '@/interfaces/SaleReceipt';
import { ServiceError } from '@/exceptions';
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
import SaleReceiptNotifyBySms from '@/services/Sales/SaleReceiptNotifyBySms';
import CheckPolicies from '@/api/middleware/CheckPolicies';
import { AbilitySubject, SaleReceiptAction } from '@/interfaces';
import { SaleReceiptApplication } from '@/services/Sales/Receipts/SaleReceiptApplication';
@Service()
export default class SalesReceiptsController extends BaseController {
@Inject()
private saleReceiptsApplication: SaleReceiptApplication;
saleReceiptService: SaleReceiptService;
@Inject()
private dynamicListService: DynamicListingService;
saleReceiptsPdf: SaleReceiptsPdfService;
@Inject()
dynamicListService: DynamicListingService;
@Inject()
saleReceiptSmsNotify: SaleReceiptNotifyBySms;
/**
* Router constructor.
*/
public router() {
router() {
const router = Router();
router.post(
@@ -97,7 +105,7 @@ export default class SalesReceiptsController extends BaseController {
* Sales receipt validation schema.
* @return {Array}
*/
private get salesReceiptsValidationSchema() {
get salesReceiptsValidationSchema() {
return [
check('customer_id').exists().isNumeric().toInt(),
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
@@ -138,14 +146,14 @@ export default class SalesReceiptsController extends BaseController {
/**
* Specific sale receipt validation schema.
*/
private get specificReceiptValidationSchema() {
get specificReceiptValidationSchema() {
return [param('id').exists().isNumeric().toInt()];
}
/**
* List sales receipts validation schema.
*/
private get listSalesReceiptsValidationSchema() {
get listSalesReceiptsValidationSchema() {
return [
query('view_slug').optional().isString().trim(),
query('stringified_filter_roles').optional().isJSON(),
@@ -162,21 +170,16 @@ export default class SalesReceiptsController extends BaseController {
* @param {Request} req
* @param {Response} res
*/
private async newSaleReceipt(
req: Request,
res: Response,
next: NextFunction
) {
async newSaleReceipt(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const saleReceiptDTO: ISaleReceiptDTO = this.matchedBodyData(req);
try {
// Store the given sale receipt details with associated entries.
const storedSaleReceipt =
await this.saleReceiptsApplication.createSaleReceipt(
tenantId,
saleReceiptDTO
);
const storedSaleReceipt = await this.saleReceiptService.createSaleReceipt(
tenantId,
saleReceiptDTO
);
return res.status(200).send({
id: storedSaleReceipt.id,
message: 'Sale receipt has been created successfully.',
@@ -191,20 +194,13 @@ export default class SalesReceiptsController extends BaseController {
* @param {Request} req
* @param {Response} res
*/
private async deleteSaleReceipt(
req: Request,
res: Response,
next: NextFunction
) {
async deleteSaleReceipt(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { id: saleReceiptId } = req.params;
try {
// Deletes the sale receipt.
await this.saleReceiptsApplication.deleteSaleReceipt(
tenantId,
saleReceiptId
);
await this.saleReceiptService.deleteSaleReceipt(tenantId, saleReceiptId);
return res.status(200).send({
id: saleReceiptId,
@@ -221,18 +217,14 @@ export default class SalesReceiptsController extends BaseController {
* @param {Request} req -
* @param {Response} res -
*/
private async editSaleReceipt(
req: Request,
res: Response,
next: NextFunction
) {
async editSaleReceipt(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { id: saleReceiptId } = req.params;
const saleReceipt = this.matchedBodyData(req);
try {
// Update the given sale receipt details.
await this.saleReceiptsApplication.editSaleReceipt(
await this.saleReceiptService.editSaleReceipt(
tenantId,
saleReceiptId,
saleReceipt
@@ -252,20 +244,13 @@ export default class SalesReceiptsController extends BaseController {
* @param {Response} res
* @param {NextFunction} next
*/
private async closeSaleReceipt(
req: Request,
res: Response,
next: NextFunction
) {
async closeSaleReceipt(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { id: saleReceiptId } = req.params;
try {
// Update the given sale receipt details.
await this.saleReceiptsApplication.closeSaleReceipt(
tenantId,
saleReceiptId
);
await this.saleReceiptService.closeSaleReceipt(tenantId, saleReceiptId);
return res.status(200).send({
id: saleReceiptId,
message: 'Sale receipt has been closed successfully.',
@@ -280,11 +265,7 @@ export default class SalesReceiptsController extends BaseController {
* @param {Request} req
* @param {Response} res
*/
private async getSalesReceipts(
req: Request,
res: Response,
next: NextFunction
) {
async getSalesReceipts(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const filter = {
sortOrder: 'desc',
@@ -293,9 +274,10 @@ export default class SalesReceiptsController extends BaseController {
pageSize: 12,
...this.matchedQueryData(req),
};
try {
const { data, pagination, filterMeta } =
await this.saleReceiptsApplication.getSaleReceipts(tenantId, filter);
await this.saleReceiptService.salesReceiptsList(tenantId, filter);
const response = this.transfromToResponse({
data,
@@ -319,10 +301,11 @@ export default class SalesReceiptsController extends BaseController {
const { tenantId } = req;
try {
const saleReceipt = await this.saleReceiptsApplication.getSaleReceipt(
const saleReceipt = await this.saleReceiptService.getSaleReceipt(
tenantId,
saleReceiptId
);
res.format({
'application/json': () => {
return res
@@ -330,11 +313,10 @@ export default class SalesReceiptsController extends BaseController {
.send(this.transfromToResponse({ saleReceipt }));
},
'application/pdf': async () => {
const pdfContent =
await this.saleReceiptsApplication.getSaleReceiptPdf(
tenantId,
saleReceipt
);
const pdfContent = await this.saleReceiptsPdf.saleReceiptPdf(
tenantId,
saleReceipt
);
res.set({
'Content-Type': 'application/pdf',
'Content-Length': pdfContent.length,
@@ -362,11 +344,10 @@ export default class SalesReceiptsController extends BaseController {
const { id: receiptId } = req.params;
try {
const saleReceipt =
await this.saleReceiptsApplication.saleReceiptNotifyBySms(
tenantId,
receiptId
);
const saleReceipt = await this.saleReceiptSmsNotify.notifyBySms(
tenantId,
receiptId
);
return res.status(200).send({
id: saleReceipt.id,
message:
@@ -392,11 +373,10 @@ export default class SalesReceiptsController extends BaseController {
const { id: receiptId } = req.params;
try {
const smsDetails =
await this.saleReceiptsApplication.getSaleReceiptSmsDetails(
tenantId,
receiptId
);
const smsDetails = await this.saleReceiptSmsNotify.smsDetails(
tenantId,
receiptId
);
return res.status(200).send({
data: smsDetails,
});

View File

@@ -1,10 +1,10 @@
import { Router } from 'express';
import { Container, Service } from 'typedi';
import SalesInvoices from './SalesInvoices'
import SalesEstimates from './SalesEstimates';
import SalesReceipts from './SalesReceipts';
import CreditNotes from './CreditNotes';
import SalesInvoices from './SalesInvoices'
import PaymentReceives from './PaymentReceives';
import CreditNotes from './CreditNotes';
@Service()
export default class SalesController {
/**

View File

@@ -1,278 +0,0 @@
import { Inject, Service } from 'typedi';
import { Router, Request, Response } from 'express';
import { body, param } from 'express-validator';
import BaseController from '@/api/controllers/BaseController';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import { TaxRatesApplication } from '@/services/TaxRates/TaxRatesApplication';
import CheckAbilities from '@/api/middleware/CheckPolicies';
import { ServiceError } from '@/exceptions';
import { ERRORS } from '@/services/TaxRates/constants';
import { AbilitySubject, TaxRateAction } from '@/interfaces';
@Service()
export class TaxRatesController extends BaseController {
@Inject()
private taxRatesApplication: TaxRatesApplication;
/**
* Router constructor.
*/
public router() {
const router = Router();
router.post(
'/',
CheckAbilities(TaxRateAction.CREATE, AbilitySubject.TaxRate),
this.taxRateValidationSchema,
this.validationResult,
asyncMiddleware(this.createTaxRate.bind(this)),
this.handleServiceErrors
);
router.post(
'/:id',
CheckAbilities(TaxRateAction.EDIT, AbilitySubject.TaxRate),
[param('id').exists().toInt(), ...this.taxRateValidationSchema],
this.validationResult,
asyncMiddleware(this.editTaxRate.bind(this)),
this.handleServiceErrors
);
router.post(
'/:id/active',
CheckAbilities(TaxRateAction.EDIT, AbilitySubject.TaxRate),
[param('id').exists().toInt()],
this.validationResult,
asyncMiddleware(this.activateTaxRate.bind(this)),
this.handleServiceErrors
);
router.post(
'/:id/inactive',
CheckAbilities(TaxRateAction.EDIT, AbilitySubject.TaxRate),
[param('id').exists().toInt()],
this.validationResult,
asyncMiddleware(this.inactivateTaxRate.bind(this)),
this.handleServiceErrors
);
router.delete(
'/:id',
CheckAbilities(TaxRateAction.DELETE, AbilitySubject.TaxRate),
[param('id').exists().toInt()],
this.validationResult,
asyncMiddleware(this.deleteTaxRate.bind(this)),
this.handleServiceErrors
);
router.get(
'/:id',
CheckAbilities(TaxRateAction.VIEW, AbilitySubject.TaxRate),
[param('id').exists().toInt()],
this.validationResult,
asyncMiddleware(this.getTaxRate.bind(this)),
this.handleServiceErrors
);
router.get(
'/',
CheckAbilities(TaxRateAction.VIEW, AbilitySubject.TaxRate),
this.validationResult,
asyncMiddleware(this.getTaxRates.bind(this)),
this.handleServiceErrors
);
return router;
}
/**
* Tax rate validation schema.
*/
private get taxRateValidationSchema() {
return [
body('name').exists(),
body('code').exists().isString(),
body('rate').exists().isNumeric().toFloat(),
body('description').optional().trim().isString(),
body('is_non_recoverable').optional().isBoolean().default(false),
body('is_compound').optional().isBoolean().default(false),
body('active').optional().isBoolean().default(false),
];
}
/**
* Creates a new tax rate.
* @param {Request} req -
* @param {Response} res -
*/
public async createTaxRate(req: Request, res: Response, next) {
const { tenantId } = req;
const createTaxRateDTO = this.matchedBodyData(req);
try {
const taxRate = await this.taxRatesApplication.createTaxRate(
tenantId,
createTaxRateDTO
);
return res.status(200).send({
data: taxRate,
});
} catch (error) {
next(error);
}
}
/**
* Edits the given tax rate.
* @param {Request} req -
* @param {Response} res -
*/
public async editTaxRate(req: Request, res: Response, next) {
const { tenantId } = req;
const editTaxRateDTO = this.matchedBodyData(req);
const { id: taxRateId } = req.params;
try {
const taxRate = await this.taxRatesApplication.editTaxRate(
tenantId,
taxRateId,
editTaxRateDTO
);
return res.status(200).send({
data: taxRate,
});
} catch (error) {
next(error);
}
}
/**
* Deletes the given tax rate.
* @param {Request} req -
* @param {Response} res -
*/
public async deleteTaxRate(req: Request, res: Response, next) {
const { tenantId } = req;
const { id: taxRateId } = req.params;
try {
await this.taxRatesApplication.deleteTaxRate(tenantId, taxRateId);
return res.status(200).send({
code: 200,
message: 'The tax rate has been deleted successfully.',
});
} catch (error) {
next(error);
}
}
/**
* Retrieves the given tax rate.
* @param {Request} req -
* @param {Response} res -
*/
public async getTaxRate(req: Request, res: Response, next) {
const { tenantId } = req;
const { id: taxRateId } = req.params;
try {
const taxRate = await this.taxRatesApplication.getTaxRate(
tenantId,
taxRateId
);
return res.status(200).send({ data: taxRate });
} catch (error) {
next(error);
}
}
/**
* Retrieves the tax rates list.
* @param {Request} req -
* @param {Response} res -
*/
public async getTaxRates(req: Request, res: Response, next) {
const { tenantId } = req;
try {
const taxRates = await this.taxRatesApplication.getTaxRates(tenantId);
return res.status(200).send({ data: taxRates });
} catch (error) {
next(error);
}
}
/**
* Inactivates the given tax rate.
* @param req
* @param res
* @param next
* @returns
*/
public async inactivateTaxRate(req: Request, res: Response, next) {
const { tenantId } = req;
const { id: taxRateId } = req.params;
try {
await this.taxRatesApplication.inactivateTaxRate(tenantId, taxRateId);
return res.status(200).send({
id: taxRateId,
message: 'The given tax rate has been inactivated successfully.',
});
} catch (error) {
next(error);
}
}
/**
* Inactivates the given tax rate.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns
*/
public async activateTaxRate(req: Request, res: Response, next) {
const { tenantId } = req;
const { id: taxRateId } = req.params;
try {
await this.taxRatesApplication.activateTaxRate(tenantId, taxRateId);
return res.status(200).send({
id: taxRateId,
message: 'The given tax rate has been activated successfully.',
});
} catch (error) {
next(error);
}
}
/**
* Handles service errors.
* @param {Error} error
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
private handleServiceErrors(error: Error, req: Request, res: Response, next) {
if (error instanceof ServiceError) {
if (error.errorType === ERRORS.TAX_CODE_NOT_UNIQUE) {
return res.boom.badRequest(null, {
errors: [{ type: ERRORS.TAX_CODE_NOT_UNIQUE, code: 100 }],
});
}
if (error.errorType === ERRORS.TAX_RATE_NOT_FOUND) {
return res.boom.badRequest(null, {
errors: [{ type: ERRORS.TAX_RATE_NOT_FOUND, code: 200 }],
});
}
if (error.errorType === ERRORS.TAX_RATE_ALREADY_INACTIVE) {
return res.boom.badRequest(null, {
errors: [{ type: ERRORS.TAX_RATE_ALREADY_INACTIVE, code: 300 }],
});
}
if (error.errorType === ERRORS.TAX_RATE_ALREADY_ACTIVE) {
return res.boom.badRequest(null, {
errors: [{ type: ERRORS.TAX_RATE_ALREADY_ACTIVE, code: 400 }],
});
}
}
next(error);
}
}

View File

@@ -55,7 +55,6 @@ import { InventoryItemsCostController } from './controllers/Inventory/Inventorty
import { ProjectsController } from './controllers/Projects/Projects';
import { ProjectTasksController } from './controllers/Projects/Tasks';
import { ProjectTimesController } from './controllers/Projects/Times';
import { TaxRatesController } from './controllers/TaxRates/TaxRates';
export default () => {
const app = Router();
@@ -130,7 +129,6 @@ export default () => {
);
dashboard.use('/warehouses', Container.get(WarehousesController).router());
dashboard.use('/projects', Container.get(ProjectsController).router());
dashboard.use('/tax-rates', Container.get(TaxRatesController).router());
dashboard.use('/', Container.get(ProjectTasksController).router());
dashboard.use('/', Container.get(ProjectTimesController).router());

View File

@@ -4,7 +4,6 @@ import moment from 'moment';
global.__root_dir = path.join(__dirname, '..');
global.__resources_dir = path.join(global.__root_dir, 'resources');
global.__locales_dir = path.join(global.__resources_dir, 'locales');
global.__views_dir = path.join(global.__root_dir, 'views');
moment.prototype.toMySqlDateTime = function () {
return this.format('YYYY-MM-DD HH:mm:ss');

View File

@@ -1,12 +1,8 @@
import dotenv from 'dotenv';
import path from 'path';
import { toInteger } from 'lodash';
import { castCommaListEnvVarToArray, parseBoolean } from '@/utils';
dotenv.config();
const API_RATE_LIMIT = process.env.API_RATE_LIMIT?.split(',') || [];
module.exports = {
/**
* Your favorite port
@@ -98,15 +94,16 @@ module.exports = {
* JWT secret.
*/
jwtSecret: process.env.JWT_SECRET,
resetPasswordSeconds: 600,
/**
*
*/
resetPasswordSeconds: 600,
customerSuccess: {
email: 'success@bigcapital.ly',
phoneNumber: '(218) 92 791 8381',
},
/**
* Application base URL.
*/
baseURL: process.env.BASE_URL,
/**
@@ -133,23 +130,20 @@ module.exports = {
blockDuration: 60 * 15,
},
requests: {
points: API_RATE_LIMIT[0] ? toInteger(API_RATE_LIMIT[0]) : 120,
duration: API_RATE_LIMIT[1] ? toInteger(API_RATE_LIMIT[1]) : 60,
blockDuration: API_RATE_LIMIT[2] ? toInteger(API_RATE_LIMIT[2]) : 60 * 10,
points: 60,
duration: 60,
blockDuration: 60 * 10,
},
},
/**
* Sign-up restrictions
* Users registeration configuration.
*/
signupRestrictions: {
disabled: parseBoolean<boolean>(process.env.SIGNUP_DISABLED, false),
allowedDomains: castCommaListEnvVarToArray(
process.env.SIGNUP_ALLOWED_DOMAINS
),
allowedEmails: castCommaListEnvVarToArray(
process.env.SIGNUP_ALLOWED_EMAILS
),
registration: {
countries: {
whitelist: ['LY'],
blacklist: [],
},
},
/**
@@ -159,6 +153,8 @@ module.exports = {
browserWSEndpoint: process.env.BROWSER_WS_ENDPOINT,
},
protocol: '',
hostname: '',
scheduleComputeItemCost: 'in 5 seconds',
/**

View File

@@ -1,28 +0,0 @@
export const TransactionTypes = {
SaleInvoice: 'Sale invoice',
SaleReceipt: 'Sale receipt',
PaymentReceive: 'Payment receive',
Bill: 'Bill',
BillPayment: 'Payment made',
VendorOpeningBalance: 'Vendor opening balance',
CustomerOpeningBalance: 'Customer opening balance',
InventoryAdjustment: 'Inventory adjustment',
ManualJournal: 'Manual journal',
Journal: 'Manual journal',
Expense: 'Expense',
OwnerContribution: 'Owner contribution',
TransferToAccount: 'Transfer to account',
TransferFromAccount: 'Transfer from account',
OtherIncome: 'Other income',
OtherExpense: 'Other expense',
OwnerDrawing: 'Owner drawing',
InvoiceWriteOff: 'Invoice write-off',
CreditNote: 'transaction_type.credit_note',
VendorCredit: 'transaction_type.vendor_credit',
RefundCreditNote: 'transaction_type.refund_credit_note',
RefundVendorCredit: 'transaction_type.refund_vendor_credit',
LandedCost: 'transaction_type.landed_cost',
};

View File

@@ -1,52 +0,0 @@
exports.up = (knex) => {
return knex.schema
.createTable('tax_rates', (table) => {
table.increments();
table.string('name');
table.string('code');
table.decimal('rate');
table.string('description');
table.boolean('is_non_recoverable').defaultTo(false);
table.boolean('is_compound').defaultTo(false);
table.boolean('active').defaultTo(false);
table.date('deleted_at');
table.timestamps();
})
.table('items_entries', (table) => {
table.boolean('is_inclusive_tax').defaultTo(false);
table
.integer('tax_rate_id')
.unsigned()
.references('id')
.inTable('tax_rates');
table.decimal('tax_rate').unsigned();
})
.table('sales_invoices', (table) => {
table.boolean('is_inclusive_tax').defaultTo(false);
table.decimal('tax_amount_withheld');
})
.createTable('tax_rate_transactions', (table) => {
table.increments('id');
table
.integer('tax_rate_id')
.unsigned()
.references('id')
.inTable('tax_rates');
table.string('reference_type');
table.integer('reference_id');
table.decimal('rate').unsigned();
table.integer('tax_account_id').unsigned();
})
.table('accounts_transactions', (table) => {
table
.integer('tax_rate_id')
.unsigned()
.references('id')
.inTable('tax_rates');
table.decimal('tax_rate').unsigned();
});
};
exports.down = (knex) => {
return knex.schema.dropTableIfExists('tax_rates');
};

View File

@@ -3,17 +3,17 @@ import AccountsData from '../data/accounts';
export default class SeedAccounts extends TenantSeeder {
/**
* Seeds initial accounts to the organization.
* Seeds initial accounts to the organization.
*/
up(knex) {
const data = AccountsData.map((account) => ({
...account,
name: this.i18n.__(account.name),
description: this.i18n.__(account.description),
currencyCode: this.tenant.metadata.baseCurrency,
seededAt: new Date(),
})
);
const data = AccountsData.map((account) => {
return {
...account,
name: this.i18n.__(account.name),
description: this.i18n.__(account.description),
currencyCode: this.tenant.metadata.baseCurrency,
};
});
return knex('accounts').then(async () => {
// Inserts seed entries.
return knex('accounts').insert(data);

View File

@@ -8,7 +8,7 @@ export default class SeedSettings extends TenantSeeder {
up() {
const settings = [
// Orgnization settings.
{ group: 'organization', key: 'accounting_basis', value: 'accrual' },
{ group: 'organization', key: 'accounting_basis', value: 'accural' },
// Accounts settings.
{ group: 'accounts', key: 'account_code_unique', value: true },

View File

@@ -1,14 +0,0 @@
import { TenantSeeder } from '@/lib/Seeder/TenantSeeder';
import { InitialTaxRates } from '../data/TaxRates';
export default class SeedTaxRates extends TenantSeeder {
/**
* Seeds initial tax rates to the organization.
*/
up(knex) {
return knex('tax_rates').then(async () => {
// Inserts seed entries.
return knex('tax_rates').insert(InitialTaxRates);
});
}
}

View File

@@ -1,16 +0,0 @@
import { TenantSeeder } from '@/lib/Seeder/TenantSeeder';
import { InitialTaxRates } from '../data/TaxRates';
export default class UpdateTaxPayableAccount extends TenantSeeder {
/**
* Seeds initial tax rates to the organization.
*/
up(knex) {
return knex('accounts').then(async () => {
// Inserts seed entries.
return knex('accounts').where('slug', 'tax-payable').update({
account_type: 'tax-payable',
});
});
}
}

View File

@@ -1,30 +0,0 @@
export const InitialTaxRates = [
{
name: 'Tax Exempt',
code: 'TAX-EXEMPT',
description: 'Exempts goods or services from taxes.',
rate: 0,
active: 1,
},
{
name: 'Tax on Purchases',
code: 'TAX-PURCHASES',
description: 'Fee added to the cost when you buy items.',
rate: 0,
active: 1,
},
{
name: 'Tax on Sales',
code: 'TAX-SALES',
description: 'Fee added to the cost when you sell items.',
rate: 0,
active: 1,
},
{
name: 'Sales Tax on Imports',
code: 'TAX-IMPORTS',
description: 'Fee added to the cost when you sale to another country.',
rate: 0,
active: 1,
},
];

View File

@@ -1,17 +1,7 @@
export const TaxPayableAccount = {
name: 'Tax Payable',
slug: 'tax-payable',
account_type: 'tax-payable',
code: '20006',
description: '',
active: 1,
index: 1,
predefined: 1,
};
export default [
{
name: 'Bank Account',
name:'Bank Account',
slug: 'bank-account',
account_type: 'bank',
code: '10001',
@@ -21,7 +11,7 @@ export default [
predefined: 1,
},
{
name: 'Saving Bank Account',
name:'Saving Bank Account',
slug: 'saving-bank-account',
account_type: 'bank',
code: '10002',
@@ -31,7 +21,7 @@ export default [
predefined: 0,
},
{
name: 'Undeposited Funds',
name:'Undeposited Funds',
slug: 'undeposited-funds',
account_type: 'cash',
code: '10003',
@@ -41,7 +31,7 @@ export default [
predefined: 1,
},
{
name: 'Petty Cash',
name:'Petty Cash',
slug: 'petty-cash',
account_type: 'cash',
code: '10004',
@@ -51,7 +41,7 @@ export default [
predefined: 1,
},
{
name: 'Computer Equipment',
name:'Computer Equipment',
slug: 'computer-equipment',
code: '10005',
account_type: 'fixed-asset',
@@ -62,7 +52,7 @@ export default [
description: '',
},
{
name: 'Office Equipment',
name:'Office Equipment',
slug: 'office-equipment',
code: '10006',
account_type: 'fixed-asset',
@@ -73,7 +63,7 @@ export default [
description: '',
},
{
name: 'Accounts Receivable (A/R)',
name:'Accounts Receivable (A/R)',
slug: 'accounts-receivable',
account_type: 'accounts-receivable',
code: '10007',
@@ -83,7 +73,7 @@ export default [
predefined: 1,
},
{
name: 'Inventory Asset',
name:'Inventory Asset',
slug: 'inventory-asset',
code: '10008',
account_type: 'inventory',
@@ -91,13 +81,12 @@ export default [
parent_account_id: null,
index: 1,
active: 1,
description:
'An account that holds valuation of products or goods that available for sale.',
description:'An account that holds valuation of products or goods that availiable for sale.',
},
// Libilities
{
name: 'Accounts Payable (A/P)',
name:'Accounts Payable (A/P)',
slug: 'accounts-payable',
account_type: 'accounts-payable',
parent_account_id: null,
@@ -108,39 +97,38 @@ export default [
predefined: 1,
},
{
name: 'Owner A Drawings',
name:'Owner A Drawings',
slug: 'owner-drawings',
account_type: 'other-current-liability',
parent_account_id: null,
code: '20002',
description: 'Withdrawals by the owners.',
description:'Withdrawals by the owners.',
active: 1,
index: 1,
predefined: 0,
},
{
name: 'Loan',
name:'Loan',
slug: 'owner-drawings',
account_type: 'other-current-liability',
code: '20003',
description: 'Money that has been borrowed from a creditor.',
description:'Money that has been borrowed from a creditor.',
active: 1,
index: 1,
predefined: 0,
},
{
name: 'Opening Balance Liabilities',
name:'Opening Balance Liabilities',
slug: 'opening-balance-liabilities',
account_type: 'other-current-liability',
code: '20004',
description:
'This account will hold the difference in the debits and credits entered during the opening balance..',
description:'This account will hold the difference in the debits and credits entered during the opening balance..',
active: 1,
index: 1,
predefined: 0,
},
{
name: 'Revenue Received in Advance',
name:'Revenue Received in Advance',
slug: 'revenue-received-in-advance',
account_type: 'other-current-liability',
parent_account_id: null,
@@ -150,27 +138,34 @@ export default [
index: 1,
predefined: 0,
},
TaxPayableAccount,
{
name:'Sales Tax Payable',
slug: 'owner-drawings',
account_type: 'other-current-liability',
code: '20006',
description: '',
active: 1,
index: 1,
predefined: 1,
},
// Equity
{
name: 'Retained Earnings',
name:'Retained Earnings',
slug: 'retained-earnings',
account_type: 'equity',
code: '30001',
description:
'Retained earnings tracks net income from previous fiscal years.',
description:'Retained earnings tracks net income from previous fiscal years.',
active: 1,
index: 1,
predefined: 1,
},
{
name: 'Opening Balance Equity',
name:'Opening Balance Equity',
slug: 'opening-balance-equity',
account_type: 'equity',
code: '30002',
description:
'When you enter opening balances to the accounts, the amounts enter in Opening balance equity. This ensures that you have a correct trial balance sheet for your company, without even specific the second credit or debit entry.',
description:'When you enter opening balances to the accounts, the amounts enter in Opening balance equity. This ensures that you have a correct trial balance sheet for your company, without even specific the second credit or debit entry.',
active: 1,
index: 1,
predefined: 1,
@@ -186,12 +181,11 @@ export default [
predefined: 1,
},
{
name: `Drawings`,
name:`Drawings`,
slug: 'drawings',
account_type: 'equity',
code: '30003',
description:
'Goods purchased with the intention of selling these to customers',
description:'Goods purchased with the intention of selling these to customers',
active: 1,
index: 1,
predefined: 1,
@@ -199,7 +193,7 @@ export default [
// Expenses
{
name: 'Other Expenses',
name:'Other Expenses',
slug: 'other-expenses',
account_type: 'other-expense',
parent_account_id: null,
@@ -210,18 +204,18 @@ export default [
predefined: 1,
},
{
name: 'Cost of Goods Sold',
name:'Cost of Goods Sold',
slug: 'cost-of-goods-sold',
account_type: 'cost-of-goods-sold',
parent_account_id: null,
code: '40002',
description: 'Tracks the direct cost of the goods sold.',
description:'Tracks the direct cost of the goods sold.',
active: 1,
index: 1,
predefined: 1,
},
{
name: 'Office expenses',
name:'Office expenses',
slug: 'office-expenses',
account_type: 'expense',
parent_account_id: null,
@@ -232,7 +226,7 @@ export default [
predefined: 0,
},
{
name: 'Rent',
name:'Rent',
slug: 'rent',
account_type: 'expense',
parent_account_id: null,
@@ -243,30 +237,29 @@ export default [
predefined: 0,
},
{
name: 'Exchange Gain or Loss',
name:'Exchange Gain or Loss',
slug: 'exchange-grain-loss',
account_type: 'other-expense',
parent_account_id: null,
code: '40005',
description: 'Tracks the gain and losses of the exchange differences.',
description:'Tracks the gain and losses of the exchange differences.',
active: 1,
index: 1,
predefined: 1,
},
{
name: 'Bank Fees and Charges',
name:'Bank Fees and Charges',
slug: 'bank-fees-and-charges',
account_type: 'expense',
parent_account_id: null,
code: '40006',
description:
'Any bank fees levied is recorded into the bank fees and charges account. A bank account maintenance fee, transaction charges, a late payment fee are some examples.',
description: 'Any bank fees levied is recorded into the bank fees and charges account. A bank account maintenance fee, transaction charges, a late payment fee are some examples.',
active: 1,
index: 1,
predefined: 0,
},
{
name: 'Depreciation Expense',
name:'Depreciation Expense',
slug: 'depreciation-expense',
account_type: 'expense',
parent_account_id: null,
@@ -279,7 +272,7 @@ export default [
// Income
{
name: 'Sales of Product Income',
name:'Sales of Product Income',
slug: 'sales-of-product-income',
account_type: 'income',
predefined: 1,
@@ -290,7 +283,7 @@ export default [
description: '',
},
{
name: 'Sales of Service Income',
name:'Sales of Service Income',
slug: 'sales-of-service-income',
account_type: 'income',
predefined: 0,
@@ -301,7 +294,7 @@ export default [
description: '',
},
{
name: 'Uncategorized Income',
name:'Uncategorized Income',
slug: 'uncategorized-income',
account_type: 'income',
parent_account_id: null,
@@ -312,15 +305,14 @@ export default [
predefined: 1,
},
{
name: 'Other Income',
name:'Other Income',
slug: 'other-income',
account_type: 'other-income',
parent_account_id: null,
code: '50004',
description:
'The income activities are not associated to the core business.',
description:'The income activities are not associated to the core business.',
active: 1,
index: 1,
predefined: 0,
},
];
}
];

View File

@@ -1,36 +1,51 @@
import {
IAgingPeriod,
IAgingPeriodTotal,
IAgingAmount,
IAgingSummaryQuery,
IAgingSummaryTotal,
IAgingSummaryContact,
IAgingSummaryData,
IAgingAmount
} from './AgingReport';
import { INumberFormatQuery } from './FinancialStatements';
import {
INumberFormatQuery
} from './FinancialStatements';
export interface IAPAgingSummaryQuery extends IAgingSummaryQuery {
export interface IAPAgingSummaryQuery {
asDate: Date | string;
agingDaysBefore: number;
agingPeriods: number;
numberFormat: INumberFormatQuery;
vendorsIds: number[];
noneZero: boolean;
branchesIds?: number[]
}
export interface IAPAgingSummaryVendor extends IAgingSummaryContact {
vendorName: string;
}
export interface IAPAgingSummaryVendor {
vendorName: string,
current: IAgingAmount,
aging: IAgingPeriodTotal[],
total: IAgingAmount,
};
export interface IAPAgingSummaryTotal extends IAgingSummaryTotal {}
export interface IAPAgingSummaryTotal {
current: IAgingAmount,
aging: IAgingPeriodTotal[],
total: IAgingAmount,
};
export interface IAPAgingSummaryData extends IAgingSummaryData {
vendors: IAPAgingSummaryVendor[];
}
export interface IAPAgingSummaryData {
vendors: IAPAgingSummaryVendor[],
total: IAPAgingSummaryTotal,
};
export type IAPAgingSummaryColumns = IAgingPeriod[];
export interface IARAgingSummaryMeta {
baseCurrency: string;
organizationName: string;
baseCurrency: string,
organizationName: string,
}
export interface IAPAgingSummaryMeta {
baseCurrency: string;
organizationName: string;
}
baseCurrency: string,
organizationName: string,
}

View File

@@ -1,28 +1,37 @@
import {
IAgingPeriod,
IAgingSummaryQuery,
IAgingSummaryTotal,
IAgingSummaryContact,
IAgingSummaryData,
} from './AgingReport';
import { IAgingPeriod, IAgingPeriodTotal, IAgingAmount } from './AgingReport';
import { INumberFormatQuery } from './FinancialStatements';
export interface IARAgingSummaryQuery extends IAgingSummaryQuery {
export interface IARAgingSummaryQuery {
asDate: Date | string;
agingDaysBefore: number;
agingPeriods: number;
numberFormat: INumberFormatQuery;
customersIds: number[];
branchesIds: number[];
noneZero: boolean;
}
export interface IARAgingSummaryCustomer extends IAgingSummaryContact {
export interface IARAgingSummaryCustomer {
customerName: string;
current: IAgingAmount;
aging: IAgingPeriodTotal[];
total: IAgingAmount;
}
export interface IARAgingSummaryTotal extends IAgingSummaryTotal {}
export interface IARAgingSummaryTotal {
current: IAgingAmount;
aging: IAgingPeriodTotal[];
total: IAgingAmount;
}
export interface IARAgingSummaryData extends IAgingSummaryData {
export interface IARAgingSummaryData {
customers: IARAgingSummaryCustomer[];
total: IARAgingSummaryTotal;
}
export type IARAgingSummaryColumns = IAgingPeriod[];
export interface IARAgingSummaryMeta {
organizationName: string;
baseCurrency: string;
}
organizationName: string,
baseCurrency: string,
}

View File

@@ -58,7 +58,6 @@ export interface IAccountTransaction {
date: string | Date;
referenceType: string;
referenceTypeFormatted: string;
referenceId: number;
referenceNumber?: string;
@@ -77,21 +76,12 @@ export interface IAccountTransaction {
projectId?: number;
account?: IAccount;
taxRateId?: number;
taxRate?: number;
}
export interface IAccountResponse extends IAccount {}
export enum IAccountsStructureType {
Tree = 'tree',
Flat = 'flat',
}
export interface IAccountsFilter extends IDynamicListFilterDTO {
stringifiedFilterRoles?: string;
onlyInactive: boolean;
structure?: IAccountsStructureType;
}
export interface IAccountType {
@@ -153,11 +143,3 @@ export enum AccountAction {
VIEW = 'View',
TransactionsLocking = 'TransactionsLocking',
}
export enum TaxRateAction {
CREATE = 'Create',
EDIT = 'Edit',
DELETE = 'Delete',
VIEW = 'View',
}

View File

@@ -1,9 +1,6 @@
import { INumberFormatQuery } from './FinancialStatements';
export interface IAgingPeriodTotal extends IAgingPeriod {
total: IAgingAmount;
}
};
export interface IAgingAmount {
amount: number;
@@ -23,22 +20,3 @@ export interface IAgingSummaryContact {
aging: IAgingPeriodTotal[];
total: IAgingAmount;
}
export interface IAgingSummaryQuery {
asDate: Date | string;
agingDaysBefore: number;
agingPeriods: number;
numberFormat: INumberFormatQuery;
branchesIds: number[];
noneZero: boolean;
}
export interface IAgingSummaryTotal {
current: IAgingAmount;
aging: IAgingPeriodTotal[];
total: IAgingAmount;
}
export interface IAgingSummaryData {
total: IAgingSummaryTotal;
}

View File

@@ -74,8 +74,4 @@ export interface IAuthSendingResetPassword {
export interface IAuthSendedResetPassword {
user: ISystemUser,
token: string;
}
export interface IAuthGetMetaPOJO {
signupDisabled: boolean;
}

View File

@@ -44,7 +44,7 @@ export interface IBalanceSheetQuery extends IFinancialSheetBranchesQuery {
numberFormat: INumberFormatQuery;
noneTransactions: boolean;
noneZero: boolean;
basis: 'cash' | 'accrual';
basis: 'cash' | 'accural';
accountIds: number[];
percentageOfColumn: boolean;

View File

@@ -1,7 +1,7 @@
import { Knex } from 'knex';
import { IDynamicListFilterDTO } from './DynamicFilter';
import { IItemEntry, IItemEntryDTO } from './ItemEntry';
import { IBillLandedCost } from './LandedCost';
import { IBillLandedCost } from './LandedCost';
export interface IBillDTO {
vendorId: number;
billNumber: string;
@@ -99,17 +99,17 @@ export interface IBillCreatedPayload {
trx: Knex.Transaction;
}
export interface IBillCreatingPayload {
export interface IBillCreatingPayload{
tenantId: number;
billDTO: IBillDTO;
trx: Knex.Transaction;
trx: Knex.Transaction;
}
export interface IBillEditingPayload {
tenantId: number;
oldBill: IBill;
billDTO: IBillEditDTO;
trx: Knex.Transaction;
trx: Knex.Transaction;
}
export interface IBillEditedPayload {
tenantId: number;
@@ -129,7 +129,7 @@ export interface IBIllEventDeletedPayload {
export interface IBillEventDeletingPayload {
tenantId: number;
oldBill: IBill;
trx: Knex.Transaction;
trx: Knex.Transaction;
}
export enum BillAction {
Create = 'Create',
@@ -138,16 +138,3 @@ export enum BillAction {
View = 'View',
NotifyBySms = 'NotifyBySms',
}
export interface IBillOpeningPayload {
trx: Knex.Transaction;
tenantId: number;
oldBill: IBill;
}
export interface IBillOpenedPayload {
trx: Knex.Transaction;
bill: IBill;
oldBill: IBill;
tenantId: number;
}

View File

@@ -41,7 +41,7 @@ export interface ICashFlowStatementAccountMeta {
code: string;
total: ICashFlowStatementTotal;
accountType: string;
adjustmentType: string;
adjusmentType: string;
sectionType: ICashFlowStatementSectionType.ACCOUNT;
}

View File

@@ -37,7 +37,6 @@ export enum ReportsAction {
READ_INVENTORY_ITEM_DETAILS = 'read-inventory-item-details',
READ_CASHFLOW_ACCOUNT_TRANSACTION = 'read-cashflow-account-transactions',
READ_PROJECT_PROFITABILITY_SUMMARY = 'read-project-profitability-summary',
READ_SALES_TAX_LIABILITY_SUMMARY = 'read-sales-tax-liability-summary',
}
export interface IFinancialSheetBranchesQuery {

View File

@@ -18,11 +18,6 @@ export interface IItemEntry {
rate: number;
amount: number;
total: number;
amountInclusingTax: number;
amountExludingTax: number;
discountAmount: number;
landedCost: number;
allocatedCostAmount: number;
unallocatedCostAmount: number;
@@ -37,10 +32,6 @@ export interface IItemEntry {
projectRefType?: ProjectLinkRefType;
projectRefInvoicedAmount?: number;
taxRateId: number | null;
taxRate: number;
taxAmount: number;
item?: IItem;
allocatedCostEntries?: IBillLandedCostEntry[];
@@ -55,9 +46,6 @@ export interface IItemEntryDTO {
projectRefId?: number;
projectRefType?: ProjectLinkRefType;
projectRefInvoicedAmount?: number;
taxRateId?: number;
taxCode?: string;
}
export enum ProjectLinkRefType {

View File

@@ -4,8 +4,6 @@ export interface ILedger {
getEntries(): ILedgerEntry[];
filter(cb: (entry: ILedgerEntry) => boolean): ILedger;
whereAccountId(accountId: number): ILedger;
whereContactId(contactId: number): ILedger;
whereFromDate(fromDate: Date | string): ILedger;
@@ -41,16 +39,11 @@ export interface ILedgerEntry {
index: number;
indexGroup?: number;
note?: string;
userId?: number;
itemId?: number;
branchId?: number;
projectId?: number;
taxRateId?: number;
taxRate?: number;
entryId?: number;
createdAt?: Date;

View File

@@ -1,5 +1,6 @@
import { Knex } from 'knex';
import { ISystemUser } from '@/interfaces';
import { Knex } from 'knex';
import { pick } from 'lodash';
import { ILedgerEntry } from './Ledger';
import { ISaleInvoice } from './SaleInvoice';

View File

@@ -1,5 +1,5 @@
import { Knex } from 'knex';
import { IItemEntry, IItemEntryDTO } from './ItemEntry';
import { IItemEntry } from './ItemEntry';
import { IDynamicListFilterDTO } from '@/interfaces/DynamicFilter';
export interface ISaleEstimate {
@@ -29,7 +29,7 @@ export interface ISaleEstimateDTO {
estimateDate?: Date;
reference?: string;
estimateNumber?: string;
entries: IItemEntryDTO[];
entries: IItemEntry[];
note: string;
termsConditions: string;
sendToEmail: string;

View File

@@ -1,12 +1,11 @@
import { Knex } from 'knex';
import { ISystemUser, IAccount, ITaxTransaction } from '@/interfaces';
import { ISystemUser, IAccount } from '@/interfaces';
import { IDynamicListFilter } from '@/interfaces/DynamicFilter';
import { IItemEntry, IItemEntryDTO } from './ItemEntry';
export interface ISaleInvoice {
id: number;
amount: number;
amountLocal?: number;
balance: number;
paymentAmount: number;
currencyCode: string;
exchangeRate?: number;
@@ -28,21 +27,12 @@ export interface ISaleInvoice {
branchId?: number;
projectId?: number;
writtenoffAmount?: number;
writtenoffAmountLocal?: number;
localAmount?: number;
localWrittenoffAmount?: number;
writtenoffExpenseAccountId?: number;
writtenoffExpenseAccount?: IAccount;
taxAmountWithheld: number;
taxAmountWithheldLocal: number;
taxes: ITaxTransaction[];
total: number;
totalLocal: number;
subtotal: number;
subtotalLocal: number;
subtotalExludingTax: number;
}
export interface ISaleInvoiceDTO {
@@ -54,15 +44,12 @@ export interface ISaleInvoiceDTO {
exchangeRate?: number;
invoiceMessage: string;
termsConditions: string;
isTaxExclusive: boolean;
entries: IItemEntryDTO[];
delivered: boolean;
warehouseId?: number | null;
projectId?: number;
branchId?: number | null;
isInclusiveTax?: boolean;
}
export interface ISaleInvoiceCreateDTO extends ISaleInvoiceDTO {
@@ -169,7 +156,6 @@ export interface ISaleInvoiceEventDeliveredPayload {
tenantId: number;
saleInvoiceId: number;
saleInvoice: ISaleInvoice;
trx: Knex.Transaction;
}
export interface ISaleInvoiceDeliveringPayload {

View File

@@ -1,51 +0,0 @@
export interface SalesTaxLiabilitySummaryQuery {
fromDate: Date;
toDate: Date;
basis: 'cash' | 'accrual';
}
export interface SalesTaxLiabilitySummaryAmount {
amount: number;
formattedAmount: string;
currencyCode: string;
}
export interface SalesTaxLiabilitySummaryTotal {
taxableAmount: SalesTaxLiabilitySummaryAmount;
taxAmount: SalesTaxLiabilitySummaryAmount;
collectedTaxAmount: SalesTaxLiabilitySummaryAmount;
}
export interface SalesTaxLiabilitySummaryRate {
id: number;
taxName: string;
taxableAmount: SalesTaxLiabilitySummaryAmount;
taxAmount: SalesTaxLiabilitySummaryAmount;
taxPercentage: any;
collectedTaxAmount: SalesTaxLiabilitySummaryAmount;
}
export enum SalesTaxLiabilitySummaryTableRowType {
TaxRate = 'TaxRate',
Total = 'Total',
}
export interface SalesTaxLiabilitySummaryReportData {
taxRates: SalesTaxLiabilitySummaryRate[];
total: SalesTaxLiabilitySummaryTotal;
}
export type SalesTaxLiabilitySummaryPayableById = Record<
string,
{ taxRateId: number; credit: number; debit: number }
>;
export type SalesTaxLiabilitySummarySalesById = Record<
string,
{ taxRateId: number; credit: number; debit: number }
>;
export interface SalesTaxLiabilitySummaryMeta {
organizationName: string;
baseCurrency: string;
}

View File

@@ -1,88 +0,0 @@
import { Knex } from 'knex';
export interface ITaxRate {
id?: number;
name: string;
code: string;
rate: number;
description: string;
IsNonRecoverable: boolean;
IsCompound: boolean;
active: boolean;
}
export interface ICommonTaxRateDTO {
name: string;
code: string;
rate: number;
description: string;
IsNonRecoverable: boolean;
IsCompound: boolean;
active: boolean;
}
export interface ICreateTaxRateDTO extends ICommonTaxRateDTO {}
export interface IEditTaxRateDTO extends ICommonTaxRateDTO {}
export interface ITaxRateCreatingPayload {
createTaxRateDTO: ICreateTaxRateDTO;
tenantId: number;
trx: Knex.Transaction;
}
export interface ITaxRateCreatedPayload {
createTaxRateDTO: ICreateTaxRateDTO;
taxRate: ITaxRate;
tenantId: number;
trx: Knex.Transaction;
}
export interface ITaxRateEditingPayload {
editTaxRateDTO: IEditTaxRateDTO;
tenantId: number;
trx: Knex.Transaction;
}
export interface ITaxRateEditedPayload {
editTaxRateDTO: IEditTaxRateDTO;
oldTaxRate: ITaxRate;
taxRate: ITaxRate;
tenantId: number;
trx: Knex.Transaction;
}
export interface ITaxRateDeletingPayload {
oldTaxRate: ITaxRate;
tenantId: number;
trx: Knex.Transaction;
}
export interface ITaxRateActivatingPayload {
taxRateId: number;
tenantId: number;
trx: Knex.Transaction;
}
export interface ITaxRateActivatedPayload {
taxRateId: number;
tenantId: number;
trx: Knex.Transaction;
}
export interface ITaxRateDeletedPayload {
oldTaxRate: ITaxRate;
tenantId: number;
trx: Knex.Transaction;
}
export interface ITaxTransaction {
id?: number;
taxRateId: number;
referenceType: string;
referenceId: number;
rate: number;
taxAccountId: number;
}
export enum TaxRateAction {
CREATE = 'Create',
EDIT = 'Edit',
DELETE = 'Delete',
VIEW = 'View',
}

View File

@@ -4,7 +4,7 @@ export interface ITrialBalanceSheetQuery {
fromDate: Date | string;
toDate: Date | string;
numberFormat: INumberFormatQuery;
basis: 'cash' | 'accrual';
basis: 'cash' | 'accural';
noneZero: boolean;
noneTransactions: boolean;
onlyActive: boolean;

View File

@@ -1,7 +1,6 @@
import { AnyObject } from '@casl/ability/dist/types/types';
import { ITenant } from '@/interfaces';
import { Model } from 'objection';
import { Tenant } from '@/system/models';
export interface ISystemUser extends Model {
id: number;
@@ -55,52 +54,20 @@ export interface IUserInvite {
export interface IInviteUserService {
acceptInvite(token: string, inviteUserInput: IInviteUserInput): Promise<void>;
/**
* Re-send user invite.
* @param {number} tenantId -
* @param {string} email -
* @return {Promise<{ invite: IUserInvite }>}
*/
resendInvite(
tenantId: number,
userId: number,
authorizedUser: ISystemUser
): Promise<{
user: ITenantUser;
invite: IUserInvite;
}>;
/**
* Sends invite mail to the given email from the given tenant and user.
* @param {number} tenantId -
* @param {string} email -
* @param {IUser} authorizedUser -
* @return {Promise<IUserInvite>}
*/
sendInvite(
tenantId: number,
sendInviteDTO: IUserSendInviteDTO,
email: string,
authorizedUser: ISystemUser
): Promise<{
invitedUser: ITenantUser;
invite: IUserInvite;
}>;
}
export interface IAcceptInviteUserService {
/**
* Accept the received invite.
* @param {string} token
* @param {IInviteUserInput} inviteUserInput
* @throws {ServiceErrors}
* @returns {Promise<void>}
*/
acceptInvite(token: string, inviteUserDTO: IInviteUserInput): Promise<void>;
/**
* Validate the given invite token.
* @param {string} token - the given token string.
* @throws {ServiceError}
*/
checkInvite(
token: string
): Promise<{ inviteToken: IUserInvite; orgName: object }>;
@@ -154,7 +121,7 @@ export interface IUserInvitedEventPayload {
tenantId: number;
user: ITenantUser;
}
export interface IUserInviteTenantSyncedEventPayload {
export interface IUserInviteTenantSyncedEventPayload{
invite: IUserInvite;
authorizedUser: ISystemUser;
tenantId: number;
@@ -176,10 +143,10 @@ export interface IAcceptInviteEventPayload {
export interface ICheckInviteEventPayload {
inviteToken: IUserInvite;
tenant: Tenant;
tenant: ITenant
}
export interface IUserSendInviteDTO {
email: string;
roleId: number;
}
}

View File

@@ -73,7 +73,6 @@ export * from './Project';
export * from './Tasks';
export * from './Times';
export * from './ProjectProfitabilitySummary';
export * from './TaxRate';
export interface I18nService {
__: (input: string) => string;

View File

@@ -1,34 +1,38 @@
import { Container } from 'typedi';
import AuthenticationMailMesssages from '@/services/Authentication/AuthenticationMailMessages';
import { Container, Inject } from 'typedi';
import AuthenticationService from '@/services/Authentication/AuthApplication';
export default class ResetPasswordEmailJob {
export default class WelcomeEmailJob {
/**
* Constructor method.
* @param {Agenda} agenda
* @param {Agenda} agenda
*/
constructor(agenda) {
agenda.define(
'reset-password-mail',
{ priority: 'high' },
this.handler.bind(this)
this.handler.bind(this),
);
}
/**
* Handle send welcome mail job.
* @param {Job} job
* @param {Function} done
* @param {Job} job
* @param {Function} done
*/
public async handler(job, done: Function): Promise<void> {
const { data } = job.attrs;
const { user, token } = data;
const authService = Container.get(AuthenticationMailMesssages);
const Logger = Container.get('logger');
const authService = Container.get(AuthenticationService);
Logger.info(`[send_reset_password] started.`, { data });
try {
await authService.sendResetPasswordMessage(user, token);
done();
await authService.mailMessages.sendResetPasswordMessage(user, token);
Logger.info(`[send_reset_password] finished.`, { data });
done()
} catch (error) {
console.log(error);
Logger.error(`[send_reset_password] error.`, { data, error });
done(error);
}
}

View File

@@ -0,0 +1,22 @@
import { Container } from 'typedi';
export default class SmsNotification {
constructor(agenda) {
agenda.define('sms-notification', { priority: 'high' }, this.handler);
}
/**
*
* @param {Job}job
*/
async handler(job) {
const { message, to } = job.attrs.data;
const smsClient = Container.get('SMSClient');
try {
await smsClient.sendMessage(to, message);
} catch (error) {
done(e);
}
}
}

View File

@@ -1,6 +1,5 @@
import { Container, Inject } from 'typedi';
import InviteUserService from '@/services/InviteUsers/AcceptInviteUser';
import SendInviteUsersMailMessage from '@/services/InviteUsers/SendInviteUsersMailMessage';
export default class UserInviteMailJob {
/**
@@ -22,17 +21,24 @@ export default class UserInviteMailJob {
*/
public async handler(job, done: Function): Promise<void> {
const { invite, authorizedUser, tenantId } = job.attrs.data;
const sendInviteMailMessage = Container.get(SendInviteUsersMailMessage);
const Logger = Container.get('logger');
const inviteUsersService = Container.get(InviteUserService);
Logger.info(`Send invite user mail - started: ${job.attrs.data}`);
try {
await sendInviteMailMessage.sendInviteMail(
await inviteUsersService.mailMessages.sendInviteMail(
tenantId,
authorizedUser,
invite
);
Logger.info(`Send invite user mail - finished: ${job.attrs.data}`);
done();
} catch (error) {
console.log(error);
Logger.info(
`Send invite user mail - error: ${job.attrs.data}, error: ${error}`
);
done(error);
}
}

View File

@@ -0,0 +1,35 @@
import { Container, Inject } from 'typedi';
import AuthenticationService from '@/services/Authentication/AuthApplication';
export default class WelcomeSMSJob {
/**
* Constructor method.
* @param {Agenda} agenda
*/
constructor(agenda) {
agenda.define('welcome-sms', { priority: 'high' }, this.handler);
}
/**
* Handle send welcome mail job.
* @param {Job} job
* @param {Function} done
*/
public async handler(job, done: Function): Promise<void> {
const { tenant, user } = job.attrs.data;
const Logger = Container.get('logger');
const authService = Container.get(AuthenticationService);
Logger.info(`[welcome_sms] started: ${job.attrs.data}`);
try {
await authService.smsMessages.sendWelcomeMessage(tenant, user);
Logger.info(`[welcome_sms] finished`, { tenant, user });
done();
} catch (error) {
Logger.info(`[welcome_sms] error`, { error, tenant, user });
done(error);
}
}
}

View File

@@ -0,0 +1,39 @@
import { Container } from 'typedi';
import AuthenticationService from '@/services/Authentication/AuthApplication';
export default class WelcomeEmailJob {
/**
* Constructor method.
* @param {Agenda} agenda -
*/
constructor(agenda) {
// Welcome mail and SMS message.
agenda.define(
'welcome-email',
{ priority: 'high' },
this.handler.bind(this),
);
}
/**
* Handle send welcome mail job.
* @param {Job} job
* @param {Function} done
*/
public async handler(job, done: Function): Promise<void> {
const { organizationId, user } = job.attrs.data;
const Logger: any = Container.get('logger');
const authService = Container.get(AuthenticationService);
Logger.info(`[welcome_mail] started: ${job.attrs.data}`);
try {
await authService.mailMessages.sendWelcomeMessage(user, organizationId);
Logger.info(`[welcome_mail] finished: ${job.attrs.data}`);
done();
} catch (error) {
Logger.error(`[welcome_mail] error: ${job.attrs.data}, error: ${error}`);
done(error);
}
}
}

View File

@@ -1,7 +1,7 @@
import { Container } from 'typedi';
import events from '@/subscribers/events';
import SalesInvoicesCost from '@/services/Sales/SalesInvoicesCost';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import { SaleInvoicesCost } from '@/services/Sales/Invoices/SalesInvoicesCost';
export default class WriteInvoicesJournalEntries {
eventPublisher: EventPublisher;
@@ -26,7 +26,7 @@ export default class WriteInvoicesJournalEntries {
*/
public async handler(job, done: Function): Promise<void> {
const { startingDate, tenantId } = job.attrs.data;
const salesInvoicesCost = Container.get(SaleInvoicesCost);
const salesInvoicesCost = Container.get(SalesInvoicesCost);
try {
await salesInvoicesCost.writeCostLotsGLEntries(tenantId, startingDate);

View File

@@ -1,8 +1,8 @@
import { forEach, uniqBy } from 'lodash';
import DynamicFilterAbstractor from './DynamicFilterAbstractor';
import DynamicFilterAbstructor from './DynamicFilterAbstructor';
import { IDynamicFilter, IFilterRole, IModel } from '@/interfaces';
export default class DynamicFilter extends DynamicFilterAbstractor{
export default class DynamicFilter extends DynamicFilterAbstructor{
private model: IModel;
private tableName: string;
private dynamicFilters: IDynamicFilter[];

View File

@@ -1,5 +1,5 @@
export default class DynamicFilterAbstractor {
export default class DynamicFilterAbstructor {
/**
* Extract relation table name from relation.
* @param {String} column -

View File

@@ -1,7 +1,7 @@
import DynamicFilterRoleAbstractor from './DynamicFilterRoleAbstractor';
import DynamicFilterRoleAbstructor from './DynamicFilterRoleAbstructor';
import { IFilterRole } from '@/interfaces';
export default class FilterRoles extends DynamicFilterRoleAbstractor {
export default class FilterRoles extends DynamicFilterRoleAbstructor {
private filterRoles: IFilterRole[];
/**

View File

@@ -6,7 +6,7 @@ import DynamicFilterQueryParser from './DynamicFilterQueryParser';
import { Lexer } from '../LogicEvaluation/Lexer';
import { COMPARATOR_TYPE, FIELD_TYPE } from './constants';
export default abstract class DynamicFilterAbstractor
export default abstract class DynamicFilterAbstructor
implements IDynamicFilter
{
protected filterRoles: IFilterRole[] = [];

View File

@@ -1,4 +1,4 @@
import DynamicFilterRoleAbstractor from '@/lib/DynamicFilter/DynamicFilterRoleAbstractor';
import DynamicFilterRoleAbstructor from '@/lib/DynamicFilter/DynamicFilterRoleAbstructor';
import { FIELD_TYPE } from './constants';
interface ISortRole {
@@ -6,7 +6,7 @@ interface ISortRole {
order: string;
}
export default class DynamicFilterSortBy extends DynamicFilterRoleAbstractor {
export default class DynamicFilterSortBy extends DynamicFilterRoleAbstructor {
private sortRole: ISortRole = {};
/**

View File

@@ -1,8 +1,8 @@
import { omit } from 'lodash';
import { IView, IViewRole } from '@/interfaces';
import DynamicFilterRoleAbstractor from './DynamicFilterRoleAbstractor';
import DynamicFilterRoleAbstructor from './DynamicFilterRoleAbstructor';
export default class DynamicFilterViews extends DynamicFilterRoleAbstractor {
export default class DynamicFilterViews extends DynamicFilterRoleAbstructor {
private viewSlug: string;
private logicExpression: string;
private filterRoles: IViewRole[];

View File

@@ -109,7 +109,7 @@ export default class Mail {
* Retrieve view content from the view directory.
*/
private getViewContent(): string {
const filePath = path.join(global.__views_dir, `/${this.view}`);
const filePath = path.join(global.__root_dir, `../views/${this.view}`);
return fs.readFileSync(filePath, 'utf8');
}
}

View File

@@ -1,7 +1,7 @@
import moment from 'moment';
import * as R from 'ramda';
import { includes, isFunction, isObject, isUndefined, omit } from 'lodash';
import { formatNumber, sortObjectKeysAlphabetically } from 'utils';
import { formatNumber } from 'utils';
export class Transformer {
public context: any;
@@ -39,33 +39,12 @@ export class Transformer {
return object;
};
/**
*
* @param object
* @returns
*/
protected preCollectionTransform = (object: any) => {
return object;
};
/**
*
* @param object
* @returns
*/
protected postCollectionTransform = (object: any) => {
return object;
};
/**
*
*/
public work = (object: any) => {
if (Array.isArray(object)) {
const preTransformed = this.preCollectionTransform(object);
const transformed = preTransformed.map(this.getTransformation);
return this.postCollectionTransform(transformed);
return object.map(this.getTransformation);
} else if (isObject(object)) {
return this.getTransformation(object);
}
@@ -81,7 +60,6 @@ export class Transformer {
const normlizedItem = this.normalizeModelItem(item);
return R.compose(
sortObjectKeysAlphabetically,
this.transform,
R.when(this.hasExcludeAttributes, this.excludeAttributesTransformed),
this.includeAttributesTransformed

View File

@@ -1,10 +1,10 @@
import DynamicFilterRoleAbstractor from '@/lib/DynamicFilter/DynamicFilterRoleAbstractor';
import DynamicFilterRoleAbstructor from '@/lib/DynamicFilter/DynamicFilterRoleAbstructor';
import {
validateViewRoles,
buildFilterQuery,
} from '@/lib/ViewRolesBuilder';
export default class ViewRolesDynamicFilter extends DynamicFilterRoleAbstractor {
export default class ViewRolesDynamicFilter extends DynamicFilterRoleAbstructor {
/**
* Constructor method.
* @param {*} filterRoles -

View File

@@ -1,16 +1,17 @@
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import ItemSubscriber from '@/subscribers/Items/ItemSubscriber';
import InventoryAdjustmentsSubscriber from '@/subscribers/Inventory/InventoryAdjustment';
import BillWriteInventoryTransactionsSubscriber from '@/subscribers/Bills/WriteInventoryTransactions';
import PaymentSyncBillBalance from '@/subscribers/PaymentMades/PaymentSyncBillBalance';
import SaleReceiptInventoryTransactionsSubscriber from '@/subscribers/SaleReceipt/WriteInventoryTransactions';
import SaleInvoiceWriteInventoryTransactions from '@/subscribers/SaleInvoices/WriteInventoryTransactions';
import SaleInvoiceWriteGLEntriesSubscriber from '@/subscribers/SaleInvoices/WriteJournalEntries';
import SaleReceiptWriteGLEntriesSubscriber from '@/subscribers/SaleReceipt/WriteJournalEntries';
import PaymentReceiveSyncInvoices from '@/subscribers/PaymentReceive/PaymentReceiveSyncInvoices';
import CashflowTransactionSubscriber from '@/services/Cashflow/CashflowTransactionSubscriber';
import PaymentReceivesWriteGLEntriesSubscriber from '@/subscribers/PaymentReceive/WriteGLEntries';
import InventorySubscriber from '@/subscribers/Inventory/Inventory';
import SaleReceiptWriteGLEntriesSubscriber from '@/subscribers/SaleReceipt/WriteJournalEntries';
import { CustomerWriteGLOpeningBalanceSubscriber } from '@/services/Contacts/Customers/Subscribers/CustomerGLEntriesSubscriber';
import { VendorsWriteGLOpeningSubscriber } from '@/services/Contacts/Vendors/Subscribers/VendorGLEntriesSubscriber';
import SaleEstimateAutoSerialSubscriber from '@/subscribers/SaleEstimate/AutoIncrementSerial';
@@ -21,7 +22,7 @@ import SaleInvoiceAutoIncrementSubscriber from '@/subscribers/SaleInvoices/AutoI
import SaleInvoiceConvertFromEstimateSubscriber from '@/subscribers/SaleInvoices/ConvertFromEstimate';
import PaymentReceiveAutoSerialSubscriber from '@/subscribers/PaymentReceive/AutoSerialIncrement';
import SyncSystemSendInvite from '@/services/InviteUsers/SyncSystemSendInvite';
import InviteSendMainNotification from '@/services/InviteUsers/InviteSendMailNotificationSubscribe';
import InviteSendMainNotification from '@/services/InviteUsers/InviteSendMailNotification';
import SyncTenantAcceptInvite from '@/services/InviteUsers/SyncTenantAcceptInvite';
import SyncTenantUserMutate from '@/services/Users/SyncTenantUserSaved';
import { SyncTenantUserDelete } from '@/services/Users/SyncTenantUserDeleted';
@@ -30,11 +31,12 @@ import OrgBuildSmsNotificationSubscriber from '@/subscribers/Organization/BuildS
import PurgeUserAbilityCache from '@/services/Users/PurgeUserAbilityCache';
import ResetLoginThrottleSubscriber from '@/subscribers/Authentication/ResetLoginThrottle';
import AuthenticationSubscriber from '@/subscribers/Authentication/SendResetPasswordMail';
import AuthSendWelcomeMailSubscriber from '@/subscribers/Authentication/SendWelcomeMail';
import PurgeAuthorizedUserOnceRoleMutate from '@/services/Roles/PurgeAuthorizedUser';
import SendSmsNotificationToCustomer from '@/subscribers/SaleInvoices/SendSmsNotificationToCustomer';
import SendSmsNotificationSaleReceipt from '@/subscribers/SaleReceipt/SendSmsNotificationToCustomer';
import SendSmsNotificationPaymentReceive from '@/subscribers/PaymentReceive/SendSmsNotificationToCustomer';
import SaleInvoiceWriteoffSubscriber from '@/services/Sales/Invoices/SaleInvoiceWriteoffSubscriber';
import SaleInvoiceWriteoffSubscriber from '@/services/Sales/SaleInvoiceWriteoffSubscriber';
import LandedCostSyncCostTransactionsSubscriber from '@/services/Purchases/LandedCost/LandedCostSyncCostTransactionsSubscriber';
import LandedCostInventoryTransactionsSubscriber from '@/services/Purchases/LandedCost/LandedCostInventoryTransactionsSubscriber';
import CreditNoteGLEntriesSubscriber from '@/services/CreditNotes/CreditNoteGLEntriesSubscriber';
@@ -65,6 +67,7 @@ import { ActivateWarehousesSubscriber } from '@/services/Warehouses/ActivateWare
import { ManualJournalWriteGLSubscriber } from '@/services/ManualJournals/ManualJournalGLEntriesSubscriber';
import { BillGLEntriesSubscriber } from '@/services/Purchases/Bills/BillGLEntriesSubscriber';
import { PaymentWriteGLEntriesSubscriber } from '@/services/Purchases/BillPayments/BillPaymentGLEntriesSubscriber';
import BranchesIntegrationsSubscribers from '@/services/Branches/EventsProvider';
import WarehousesIntegrationsSubscribers from '@/services/Warehouses/EventsProvider';
import { WarehouseTransferAutoIncrementSubscriber } from '@/services/Warehouses/WarehousesTransfers/WarehouseTransferAutoIncrementSubscriber';
@@ -79,8 +82,6 @@ import { ProjectBillableTasksSubscriber } from '@/services/Projects/Projects/Pro
import { ProjectBillableExpensesSubscriber } from '@/services/Projects/Projects/ProjectBillableExpenseSubscriber';
import { ProjectBillableBillSubscriber } from '@/services/Projects/Projects/ProjectBillableBillSubscriber';
import { SyncActualTimeTaskSubscriber } from '@/services/Projects/Times/SyncActualTimeTaskSubscriber';
import { SaleInvoiceTaxRateValidateSubscriber } from '@/services/TaxRates/subscribers/SaleInvoiceTaxRateValidateSubscriber';
import { WriteInvoiceTaxTransactionsSubscriber } from '@/services/TaxRates/subscribers/WriteInvoiceTaxTransactionsSubscriber';
export default () => {
return new EventPublisher();
@@ -88,6 +89,7 @@ export default () => {
export const susbcribers = () => {
return [
ItemSubscriber,
InventoryAdjustmentsSubscriber,
BillWriteInventoryTransactionsSubscriber,
PaymentSyncBillBalance,
@@ -118,6 +120,7 @@ export const susbcribers = () => {
PurgeUserAbilityCache,
ResetLoginThrottleSubscriber,
AuthenticationSubscriber,
AuthSendWelcomeMailSubscriber,
PurgeAuthorizedUserOnceRoleMutate,
SendSmsNotificationToCustomer,
SendSmsNotificationSaleReceipt,
@@ -187,9 +190,5 @@ export const susbcribers = () => {
ProjectBillableTasksSubscriber,
ProjectBillableExpensesSubscriber,
ProjectBillableBillSubscriber,
// Tax Rates
SaleInvoiceTaxRateValidateSubscriber,
WriteInvoiceTaxTransactionsSubscriber,
];
};

View File

@@ -1,18 +1,24 @@
import Agenda from 'agenda';
import WelcomeEmailJob from 'jobs/welcomeEmail';
import WelcomeSMSJob from 'jobs/WelcomeSMS';
import ResetPasswordMailJob from 'jobs/ResetPasswordMail';
import ComputeItemCost from 'jobs/ComputeItemCost';
import RewriteInvoicesJournalEntries from 'jobs/WriteInvoicesJEntries';
import RewriteInvoicesJournalEntries from 'jobs/writeInvoicesJEntries';
import UserInviteMailJob from 'jobs/UserInviteMail';
import OrganizationSetupJob from 'jobs/OrganizationSetup';
import OrganizationUpgrade from 'jobs/OrganizationUpgrade';
import SmsNotification from 'jobs/SmsNotification';
export default ({ agenda }: { agenda: Agenda }) => {
new WelcomeEmailJob(agenda);
new ResetPasswordMailJob(agenda);
new WelcomeSMSJob(agenda);
new UserInviteMailJob(agenda);
new ComputeItemCost(agenda);
new RewriteInvoicesJournalEntries(agenda);
new OrganizationSetupJob(agenda);
new OrganizationUpgrade(agenda);
new SmsNotification(agenda);
agenda.start();
};

View File

@@ -58,8 +58,6 @@ import ItemWarehouseQuantity from 'models/ItemWarehouseQuantity';
import Project from 'models/Project';
import Time from 'models/Time';
import Task from 'models/Task';
import TaxRate from 'models/TaxRate';
import TaxRateTransaction from 'models/TaxRateTransaction';
export default (knex) => {
const models = {
@@ -121,8 +119,6 @@ export default (knex) => {
Project,
Time,
Task,
TaxRate,
TaxRateTransaction,
};
return mapValues(models, (model) => model.bindKnex(knex));
};

View File

@@ -152,7 +152,7 @@
"Opening Balance Liabilities": "رصيد الالتزامات الافتتاحي",
"Loan": "اقراض",
"Owner A Drawings": "مسحوبات المالك",
"An account that holds valuation of products or goods that available for sale.": "حساب يحمل قيم مخزون البضاعة أو السلع المتاحة للبيع.",
"An account that holds valuation of products or goods that availiable for sale.": "حساب يحمل قيم مخزون البضاعة أو السلع المتاحة للبيع.",
"Tracks the gain and losses of the exchange differences.": "يسجل مكاسب وخسائر فروق الصرف.",
"Any bank fees levied is recorded into the bank fees and charges account. A bank account maintenance fee, transaction charges, a late payment fee are some examples.": "يتم تسجيل أي رسوم مصرفية يتم فرضها في حساب الرسوم والمصروفات البنكية. ومن الأمثلة على ذلك رسوم صيانة الحساب المصرفي ورسوم المعاملات ورسوم الدفع المتأخر.",
"The income activities are not associated to the core business.": "لا ترتبط انشطة الدخل إلى الأعمال الأساسية.",

View File

@@ -151,7 +151,7 @@
"Opening Balance Liabilities": "Opening Balance Liabilities",
"Loan": "Loan",
"Owner A Drawings": "Owner A Drawings",
"An account that holds valuation of products or goods that available for sale.": "An account that holds valuation of products or goods that available for sale.",
"An account that holds valuation of products or goods that availiable for sale.": "An account that holds valuation of products or goods that availiable for sale.",
"Tracks the gain and losses of the exchange differences.": "Tracks the gain and losses of the exchange differences.",
"Any bank fees levied is recorded into the bank fees and charges account. A bank account maintenance fee, transaction charges, a late payment fee are some examples.": "Any bank fees levied is recorded into the bank fees and charges account. A bank account maintenance fee, transaction charges, a late payment fee are some examples.",
"The income activities are not associated to the core business.": "The income activities are not associated to the core business.",

View File

@@ -127,7 +127,7 @@ export default class Account extends mixin(TenantModel, [
},
filterAccountTypes(query, typesIds) {
if (typesIds.length > 0) {
query.whereIn('account_types.account_type_id', typesIds);
query.whereIn('account_types.accoun_type_id', typesIds);
}
},
viewRolesBuilder(query, conditionals, expression) {

View File

@@ -2,15 +2,8 @@ import { Model, raw } from 'objection';
import moment from 'moment';
import { isEmpty, castArray } from 'lodash';
import TenantModel from 'models/TenantModel';
import { getTransactionTypeLabel } from '@/utils/transactions-types';
export default class AccountTransaction extends TenantModel {
referenceType: string;
credit: number;
debit: number;
exchangeRate: number;
taxRate: number;
/**
* Table name
*/
@@ -29,23 +22,7 @@ export default class AccountTransaction extends TenantModel {
* Virtual attributes.
*/
static get virtualAttributes() {
return ['referenceTypeFormatted', 'creditLocal', 'debitLocal'];
}
/**
* Retrieves the credit amount in base currency.
* @return {number}
*/
get creditLocal() {
return this.credit * this.exchangeRate;
}
/**
* Retrieves the debit amount in base currency.
* @return {number}
*/
get debitLocal() {
return this.debit * this.exchangeRate;
return ['referenceTypeFormatted'];
}
/**
@@ -53,7 +30,40 @@ export default class AccountTransaction extends TenantModel {
* @return {string}
*/
get referenceTypeFormatted() {
return getTransactionTypeLabel(this.referenceType);
return AccountTransaction.getReferenceTypeFormatted(this.referenceType);
}
/**
* Reference type formatted.
*/
static getReferenceTypeFormatted(referenceType) {
const mapped = {
SaleInvoice: 'Sale invoice',
SaleReceipt: 'Sale receipt',
PaymentReceive: 'Payment receive',
Bill: 'Bill',
BillPayment: 'Payment made',
VendorOpeningBalance: 'Vendor opening balance',
CustomerOpeningBalance: 'Customer opening balance',
InventoryAdjustment: 'Inventory adjustment',
ManualJournal: 'Manual journal',
Journal: 'Manual journal',
Expense: 'Expense',
OwnerContribution: 'Owner contribution',
TransferToAccount: 'Transfer to account',
TransferFromAccount: 'Transfer from account',
OtherIncome: 'Other income',
OtherExpense: 'Other expense',
OwnerDrawing: 'Owner drawing',
InvoiceWriteOff: 'Invoice write-off',
CreditNote: 'transaction_type.credit_note',
VendorCredit: 'transaction_type.vendor_credit',
RefundCreditNote: 'transaction_type.refund_credit_note',
RefundVendorCredit: 'transaction_type.refund_vendor_credit',
};
return mapped[referenceType] || '';
}
/**
@@ -79,9 +89,15 @@ export default class AccountTransaction extends TenantModel {
}
},
filterDateRange(query, startDate, endDate, type = 'day') {
const dateFormat = 'YYYY-MM-DD';
const fromDate = moment(startDate).startOf(type).format(dateFormat);
const toDate = moment(endDate).endOf(type).format(dateFormat);
const dateFormat = 'YYYY-MM-DD HH:mm:ss';
const fromDate = moment(startDate)
.utcOffset(0)
.startOf(type)
.format(dateFormat);
const toDate = moment(endDate)
.utcOffset(0)
.endOf(type)
.format(dateFormat);
if (startDate) {
query.where('date', '>=', fromDate);
@@ -125,6 +141,7 @@ export default class AccountTransaction extends TenantModel {
query.modify('filterDateRange', null, toDate);
query.modify('sumationCreditDebit');
},
contactsOpeningBalance(
query,
openingDate,

View File

@@ -5,7 +5,7 @@ import TenantModel from 'models/TenantModel';
import BillSettings from './Bill.Settings';
import ModelSetting from './ModelSetting';
import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from '@/services/Purchases/Bills/constants';
import { DEFAULT_VIEWS } from '@/services/Purchases/constants';
import ModelSearchable from './ModelSearchable';
export default class Bill extends mixin(TenantModel, [

View File

@@ -97,7 +97,7 @@ export default class Branch extends TenantModel {
},
/**
* Branch may belongs to associated bills.
* Branch may belongs to assocaited bills.
*/
bills: {
relation: Model.HasManyRelation,

Some files were not shown because too many files have changed in this diff Show More