mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-13 11:20:31 +00:00
Compare commits
1 Commits
attachment
...
backup-scr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
341bcbea7d |
@@ -123,15 +123,6 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"code"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "ccantrell72",
|
|
||||||
"name": "Chris Cantrell",
|
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/104120598?v=4",
|
|
||||||
"profile": "http://www.pivoten.com",
|
|
||||||
"contributions": [
|
|
||||||
"bug"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 7,
|
"contributorsPerLine": 7,
|
||||||
|
|||||||
@@ -48,9 +48,6 @@ SIGNUP_DISABLED=false
|
|||||||
SIGNUP_ALLOWED_DOMAINS=
|
SIGNUP_ALLOWED_DOMAINS=
|
||||||
SIGNUP_ALLOWED_EMAILS=
|
SIGNUP_ALLOWED_EMAILS=
|
||||||
|
|
||||||
# Sign-up Email Confirmation
|
|
||||||
SIGNUP_EMAIL_CONFIRMATION=false
|
|
||||||
|
|
||||||
# API rate limit (points,duration,block duration).
|
# API rate limit (points,duration,block duration).
|
||||||
API_RATE_LIMIT=120,60,600
|
API_RATE_LIMIT=120,60,600
|
||||||
|
|
||||||
|
|||||||
45
.github/workflows/build-deploy-container.yml
vendored
45
.github/workflows/build-deploy-container.yml
vendored
@@ -6,13 +6,18 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
WEBAPP_IMAGE_NAME: bigcapitalhq/webapp
|
REGISTRY: ghcr.io
|
||||||
SERVER_IMAGE_NAME: bigcapitalhq/server
|
WEBAPP_IMAGE_NAME: bigcapital/bigcapital-webapp
|
||||||
|
SERVER_IMAGE_NAME: bigcapital/bigcapital-server
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-publish-webapp:
|
build-publish-webapp:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
platform:
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm64
|
||||||
name: Build and deploy webapp container
|
name: Build and deploy webapp container
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
environment: production
|
environment: production
|
||||||
@@ -25,6 +30,9 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
@@ -32,26 +40,27 @@ jobs:
|
|||||||
- name: Log in to the Container registry
|
- name: Log in to the Container registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
registry: ${{ env.REGISTRY }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GH_TOKEN }}
|
||||||
|
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
- name: Extract metadata (tags, labels) for Docker
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
||||||
with:
|
with:
|
||||||
images: ${{ env.WEBAPP_IMAGE_NAME }}
|
images: ${{ env.REGISTRY }}/${{ env.WEBAPP_IMAGE_NAME }}
|
||||||
|
|
||||||
# Builds and push the Docker image.
|
# Builds and push the Docker image.
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
context: ./
|
context: .
|
||||||
file: ./packages/webapp/Dockerfile
|
file: ./packages/webapp/Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: ${{ matrix.platform }}
|
||||||
push: true
|
push: true
|
||||||
|
tags: ghcr.io/bigcapitalhq/webapp:latest
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
tags: bigcapitalhq/webapp:latest, bigcapitalhq/webapp:${{github.ref_name}}
|
|
||||||
|
|
||||||
- name: Export digest
|
- name: Export digest
|
||||||
run: |
|
run: |
|
||||||
@@ -62,7 +71,7 @@ jobs:
|
|||||||
- name: Upload digest
|
- name: Upload digest
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: digests-webapp
|
name: digests-main-${{ env.PLATFORM_PAIR }}
|
||||||
path: /tmp/digests/*
|
path: /tmp/digests/*
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
@@ -84,6 +93,9 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
@@ -91,8 +103,9 @@ jobs:
|
|||||||
- name: Log in to the Container registry
|
- name: Log in to the Container registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
registry: ${{ env.REGISTRY }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GH_TOKEN }}
|
||||||
|
|
||||||
# Builds and push the Docker image.
|
# Builds and push the Docker image.
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
@@ -101,9 +114,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
context: ./
|
context: ./
|
||||||
file: ./packages/server/Dockerfile
|
file: ./packages/server/Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: ${{ matrix.platform }}
|
||||||
push: true
|
push: true
|
||||||
tags: bigcapitalhq/server:latest, bigcapitalhq/server:${{github.ref_name}}
|
tags: ghcr.io/bigcapitalhq/server:latest
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
- name: Export digest
|
- name: Export digest
|
||||||
@@ -115,13 +128,13 @@ jobs:
|
|||||||
- name: Upload digest
|
- name: Upload digest
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: digests-server
|
name: digests-main-${{ env.PLATFORM_PAIR }}
|
||||||
path: /tmp/digests/*
|
path: /tmp/digests/*
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
# Send notification to Slack channel.
|
# Send notification to Slack channel.
|
||||||
- name: Slack Notification built and published server container successfully.
|
- name: Slack Notification built and published server container successfully.
|
||||||
uses: rtCamp/action-slack-notify@v2
|
uses: rtCamp/action-slack-notify@v2
|
||||||
env:
|
env:
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||||
70
CHANGELOG.md
70
CHANGELOG.md
@@ -2,76 +2,6 @@
|
|||||||
|
|
||||||
All notable changes to Bigcapital server-side will be in this file.
|
All notable changes to Bigcapital server-side will be in this file.
|
||||||
|
|
||||||
## [0.16.11] - 06-05-2024
|
|
||||||
|
|
||||||
### improvements
|
|
||||||
|
|
||||||
* feat: Export resource data to csv, xlsx by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/430
|
|
||||||
* feat: User email verification after signing-up. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/426
|
|
||||||
|
|
||||||
### Fixes
|
|
||||||
* feat(repo): upgrade to latest lerna v8 and pnpm v9 by @benpsnyder in https://github.com/bigcapitalhq/bigcapital/pull/414
|
|
||||||
* feat: Update Docker Build-Push Action and Add ARM64 Support by @cloudsbird in https://github.com/bigcapitalhq/bigcapital/pull/412
|
|
||||||
* feat: Pushing docker containers by version tag by @cloudsbird in https://github.com/bigcapitalhq/bigcapital/pull/421
|
|
||||||
|
|
||||||
## [0.16.10]
|
|
||||||
|
|
||||||
* fix: Running migration Docker container on Windows by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/432
|
|
||||||
|
|
||||||
## [0.16.9]
|
|
||||||
|
|
||||||
* feat: New Relic for tracking by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/429
|
|
||||||
|
|
||||||
## [0.16.8]
|
|
||||||
|
|
||||||
* feat: Ability to enable/disable the bank connect feature by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/423
|
|
||||||
|
|
||||||
## [0.16.6]
|
|
||||||
|
|
||||||
* hotfix: fix the subscription plan when subscribe on cloud by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/422
|
|
||||||
|
|
||||||
## [0.16.5]
|
|
||||||
|
|
||||||
IMPORTANT: If you upgraded to the v0.16 recently you should upgrade to v0.16.4 as soon as possible, because there're some breaking changes affected the sign-in and some users reported couldn't sign-in.
|
|
||||||
|
|
||||||
* feat: Seed free subscription to tenants that have no subscription. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/410
|
|
||||||
|
|
||||||
## [0.16.3]
|
|
||||||
|
|
||||||
* feat: Integrate Lemon Squeezy payment by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/402
|
|
||||||
* feat: optimize the onboarding subscription experience. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/404
|
|
||||||
* feat: subscription page content by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/405
|
|
||||||
* feat: auto subscribe to free plan once signup on community version. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/406
|
|
||||||
* chore: add default value to env variable by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/407
|
|
||||||
* fix: absolute storage imports path. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/408
|
|
||||||
|
|
||||||
## [0.16.0]
|
|
||||||
|
|
||||||
* feat: add convert to invoice button on estimate drawer toolbar by @ANasouf in https://github.com/bigcapitalhq/bigcapital/pull/361
|
|
||||||
* feat(webapp): add mark as delivered to action bar of invoice details … by @ANasouf in https://github.com/bigcapitalhq/bigcapital/pull/360
|
|
||||||
* feat(webapp): Dialog to choose the bank service provider by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/378
|
|
||||||
* feat: Categorize the bank synced transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/377
|
|
||||||
* feat: uncategorize the cashflow transaction by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/381
|
|
||||||
* Import resources from csv/xlsx by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/382
|
|
||||||
* feat(webapp): import resource UI by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/386
|
|
||||||
* fix: import resources improvements by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/388
|
|
||||||
* feat: add sample sheet to accounts and bank transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/389
|
|
||||||
* fix: show the unique row value in the import preview by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/392
|
|
||||||
* feat: advanced parser for numeric and boolean import values by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/394
|
|
||||||
* feat: validate the given imported sheet whether is empty by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/395
|
|
||||||
* feat: linking relation with id in importing by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/393
|
|
||||||
* feat: Aggregate rows import by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/396
|
|
||||||
* feat: clean up the imported temp files by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/400
|
|
||||||
* feat: add hints to import fields by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/401
|
|
||||||
|
|
||||||
## [0.15.0]
|
|
||||||
|
|
||||||
* feat: Printing financial reports by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/363
|
|
||||||
* feat: Convert invoice status after sending mail notification by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/332
|
|
||||||
* feat: Bigcapital <> Plaid Integration by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/346
|
|
||||||
* fix: Broken transactions by vendor report by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/369
|
|
||||||
* fix: Optimize the print style some financial reports by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/370
|
|
||||||
|
|
||||||
## [0.14.0] - 30-01-2024
|
## [0.14.0] - 30-01-2024
|
||||||
|
|
||||||
* feat: purchases by items exporting by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/327
|
* feat: purchases by items exporting by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/327
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://bigcapital.app" target="_blank">
|
<a href="https://bigcapital.ly" target="_blank">
|
||||||
<img src="https://raw.githubusercontent.com/abouolia/blog/main/public/bigcapital.svg" alt="Bigcapital" width="280" height="75">
|
<img src="https://raw.githubusercontent.com/abouolia/blog/main/public/bigcapital.svg" alt="Bigcapital" width="280" height="75">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://my.bigcapital.app">Bigcapital Cloud</a>
|
<a href="https://app.bigcapital.ly">Bigcapital Cloud</a>
|
||||||
</p>
|
</p>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ Bigcapital is available open-source under AGPL license. You can host it on your
|
|||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
|
|
||||||
To get started with self-hosted with Docker and Docker Compose, take a look at the [Docker guide](https://docs.bigcapital.app/deployment/docker).
|
To get started with self-hosted with Docker and Docker Compose, take a look at the [Docker guide](https://docs.bigcapital.ly/deployment/docker).
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ You can integrate Bigcapital API with your system to organize your transactions
|
|||||||
|
|
||||||
# Resources
|
# Resources
|
||||||
|
|
||||||
- [Documentation](https://docs.bigcapital.app/) - Learn how to use.
|
- [Documentation](https://docs.bigcapital.ly/) - Learn how to use.
|
||||||
- [Contribution](https://github.com/bigcapitalhq/bigcapital/blob/develop/CONTRIBUTING.md) - Welcome to any contributions.
|
- [Contribution](https://github.com/bigcapitalhq/bigcapital/blob/develop/CONTRIBUTING.md) - Welcome to any contributions.
|
||||||
- [Discord](https://discord.com/invite/c8nPBJafeb) - Ask for help.
|
- [Discord](https://discord.com/invite/c8nPBJafeb) - Ask for help.
|
||||||
- [Bug Tracker](https://github.com/bigcapitalhq/bigcapital/issues) - Notify us new bugs.
|
- [Bug Tracker](https://github.com/bigcapitalhq/bigcapital/issues) - Notify us new bugs.
|
||||||
@@ -124,7 +124,6 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|||||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/asenawritescode"><img src="https://avatars.githubusercontent.com/u/67445192?v=4?s=100" width="100px;" alt="Asena"/><br /><sub><b>Asena</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Aasenawritescode" title="Bug reports">🐛</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/asenawritescode"><img src="https://avatars.githubusercontent.com/u/67445192?v=4?s=100" width="100px;" alt="Asena"/><br /><sub><b>Asena</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Aasenawritescode" title="Bug reports">🐛</a></td>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="https://snyder.tech"><img src="https://avatars.githubusercontent.com/u/707567?v=4?s=100" width="100px;" alt="Ben Snyder"/><br /><sub><b>Ben Snyder</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=benpsnyder" title="Code">💻</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://snyder.tech"><img src="https://avatars.githubusercontent.com/u/707567?v=4?s=100" width="100px;" alt="Ben Snyder"/><br /><sub><b>Ben Snyder</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=benpsnyder" title="Code">💻</a></td>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="http://vederis.id"><img src="https://avatars.githubusercontent.com/u/13505006?v=4?s=100" width="100px;" alt="Vederis Leunardus"/><br /><sub><b>Vederis Leunardus</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=cloudsbird" title="Code">💻</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="http://vederis.id"><img src="https://avatars.githubusercontent.com/u/13505006?v=4?s=100" width="100px;" alt="Vederis Leunardus"/><br /><sub><b>Vederis Leunardus</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=cloudsbird" title="Code">💻</a></td>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="http://www.pivoten.com"><img src="https://avatars.githubusercontent.com/u/104120598?v=4?s=100" width="100px;" alt="Chris Cantrell"/><br /><sub><b>Chris Cantrell</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Accantrell72" title="Bug reports">🐛</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -25,12 +25,12 @@ services:
|
|||||||
|
|
||||||
webapp:
|
webapp:
|
||||||
container_name: bigcapital-webapp
|
container_name: bigcapital-webapp
|
||||||
image: bigcapitalhq/webapp:latest
|
image: ghcr.io/bigcapitalhq/webapp:latest
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
|
|
||||||
server:
|
server:
|
||||||
container_name: bigcapital-server
|
container_name: bigcapital-server
|
||||||
image: bigcapitalhq/server:latest
|
image: ghcr.io/bigcapitalhq/server:latest
|
||||||
expose:
|
expose:
|
||||||
- '3000'
|
- '3000'
|
||||||
links:
|
links:
|
||||||
@@ -102,14 +102,6 @@ services:
|
|||||||
- LEMONSQUEEZY_WEBHOOK_SECRET=${LEMONSQUEEZY_WEBHOOK_SECRET}
|
- LEMONSQUEEZY_WEBHOOK_SECRET=${LEMONSQUEEZY_WEBHOOK_SECRET}
|
||||||
- HOSTED_ON_BIGCAPITAL_CLOUD=${HOSTED_ON_BIGCAPITAL_CLOUD}
|
- HOSTED_ON_BIGCAPITAL_CLOUD=${HOSTED_ON_BIGCAPITAL_CLOUD}
|
||||||
|
|
||||||
# New Relic matrics tracking.
|
|
||||||
- NEW_RELIC_DISTRIBUTED_TRACING_ENABLED=${NEW_RELIC_DISTRIBUTED_TRACING_ENABLED}
|
|
||||||
- NEW_RELIC_LOG=${NEW_RELIC_LOG}
|
|
||||||
- NEW_RELIC_AI_MONITORING_ENABLED=${NEW_RELIC_AI_MONITORING_ENABLED}
|
|
||||||
- NEW_RELIC_CUSTOM_INSIGHTS_EVENTS_MAX_SAMPLES_STORED=${NEW_RELIC_CUSTOM_INSIGHTS_EVENTS_MAX_SAMPLES_STORED}
|
|
||||||
- NEW_RELIC_SPAN_EVENTS_MAX_SAMPLES_STORED=${NEW_RELIC_SPAN_EVENTS_MAX_SAMPLES_STORED}
|
|
||||||
|
|
||||||
|
|
||||||
database_migration:
|
database_migration:
|
||||||
container_name: bigcapital-database-migration
|
container_name: bigcapital-database-migration
|
||||||
build:
|
build:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM bigcapitalhq/server:latest as build
|
FROM ghcr.io/bigcapitalhq/server:latest as build
|
||||||
|
|
||||||
ARG DB_HOST= \
|
ARG DB_HOST= \
|
||||||
DB_USER= \
|
DB_USER= \
|
||||||
@@ -34,5 +34,7 @@ WORKDIR /app/packages/server
|
|||||||
|
|
||||||
RUN git clone https://github.com/vishnubob/wait-for-it.git
|
RUN git clone https://github.com/vishnubob/wait-for-it.git
|
||||||
|
|
||||||
# Once we listen the mysql port run the migration task.
|
ADD docker/migration/start.sh /
|
||||||
CMD ./wait-for-it/wait-for-it.sh mysql:3306 -- sh -c "node ./build/commands.js system:migrate:latest && node ./build/commands.js tenants:migrate:latest"
|
RUN chmod +x /start.sh
|
||||||
|
|
||||||
|
CMD ["/start.sh"]
|
||||||
5
docker/migration/start.sh
Normal file
5
docker/migration/start.sh
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Migrate the master system database.
|
||||||
|
./wait-for-it/wait-for-it.sh mysql:3306 -- node ./build/commands.js system:migrate:latest
|
||||||
|
|
||||||
|
# Migrate all tenants.
|
||||||
|
./wait-for-it/wait-for-it.sh mysql:3306 -- node ./build/commands.js tenants:migrate:latest
|
||||||
@@ -78,9 +78,6 @@ ENV MAIL_HOST=$MAIL_HOST \
|
|||||||
SIGNUP_ALLOWED_DOMAINS=$SIGNUP_ALLOWED_DOMAINS \
|
SIGNUP_ALLOWED_DOMAINS=$SIGNUP_ALLOWED_DOMAINS \
|
||||||
SIGNUP_ALLOWED_EMAILS=$SIGNUP_ALLOWED_EMAILS
|
SIGNUP_ALLOWED_EMAILS=$SIGNUP_ALLOWED_EMAILS
|
||||||
|
|
||||||
# New Relic config file.
|
|
||||||
ENV NEW_RELIC_NO_CONFIG_FILE=true
|
|
||||||
|
|
||||||
# Create app directory.
|
# Create app directory.
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
@@ -92,8 +89,8 @@ RUN npm install -g pnpm
|
|||||||
# Copy application dependency manifests to the container image.
|
# Copy application dependency manifests to the container image.
|
||||||
COPY ./package*.json ./
|
COPY ./package*.json ./
|
||||||
COPY ./pnpm-lock.yaml ./pnpm-lock.yaml
|
COPY ./pnpm-lock.yaml ./pnpm-lock.yaml
|
||||||
COPY ./lerna.json ./lerna.json
|
|
||||||
COPY ./pnpm-workspace.yaml ./pnpm-workspace.yaml
|
COPY ./pnpm-workspace.yaml ./pnpm-workspace.yaml
|
||||||
|
COPY ./lerna.json ./lerna.json
|
||||||
COPY ./packages/server/package*.json ./packages/server/
|
COPY ./packages/server/package*.json ./packages/server/
|
||||||
|
|
||||||
# Install application dependencies
|
# Install application dependencies
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":4846,"time":"2024-05-15T09:09:09.492Z","msg":"Unable to find configuration file. If a configuration file is desired (common for non-containerized environments), a base configuration file can be copied from /Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/newrelic.js and renamed to \"newrelic.js\" in the directory from which you will start your application. Attempting to start agent using environment variables."}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":4846,"time":"2024-05-15T09:09:09.514Z","msg":"Using New Relic for Node.js. Agent version: 11.16.0; Node version: v18.18.2."}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":4846,"time":"2024-05-15T09:09:09.791Z","msg":"Using AsyncLocalContextManager"}
|
|
||||||
{"v":0,"level":50,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":4846,"time":"2024-05-15T09:09:09.793Z","msg":"New Relic for Node.js was unable to bootstrap itself due to an error:","stack":"Error: New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!\n at createAgent (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:160:11)\n at initialize (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:105:15)\n at Object.<anonymous> (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:39:3)\n at Module._compile (node:internal/modules/cjs/loader:1256:14)\n at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)\n at Module.load (node:internal/modules/cjs/loader:1119:32)\n at Module._load (node:internal/modules/cjs/loader:960:12)\n at Module.require (node:internal/modules/cjs/loader:1143:19)\n at require (node:internal/modules/cjs/helpers:119:18)\n at Object.newrelic (/Users/ahmedbouhuolia/repos/bigcapital/packages/server/build/index.js:15954:18)","message":"New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!"}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":4883,"time":"2024-05-15T09:09:27.758Z","msg":"Unable to find configuration file. If a configuration file is desired (common for non-containerized environments), a base configuration file can be copied from /Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/newrelic.js and renamed to \"newrelic.js\" in the directory from which you will start your application. Attempting to start agent using environment variables."}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":4883,"time":"2024-05-15T09:09:27.771Z","msg":"Using New Relic for Node.js. Agent version: 11.16.0; Node version: v18.18.2."}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":4883,"time":"2024-05-15T09:09:27.927Z","msg":"Using AsyncLocalContextManager"}
|
|
||||||
{"v":0,"level":50,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":4883,"time":"2024-05-15T09:09:27.929Z","msg":"New Relic for Node.js was unable to bootstrap itself due to an error:","stack":"Error: New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!\n at createAgent (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:160:11)\n at initialize (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:105:15)\n at Object.<anonymous> (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:39:3)\n at Module._compile (node:internal/modules/cjs/loader:1256:14)\n at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)\n at Module.load (node:internal/modules/cjs/loader:1119:32)\n at Module._load (node:internal/modules/cjs/loader:960:12)\n at Module.require (node:internal/modules/cjs/loader:1143:19)\n at require (node:internal/modules/cjs/helpers:119:18)\n at Object.newrelic (/Users/ahmedbouhuolia/repos/bigcapital/packages/server/build/index.js:15954:18)","message":"New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!"}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":5411,"time":"2024-05-15T09:15:10.318Z","msg":"Unable to find configuration file. If a configuration file is desired (common for non-containerized environments), a base configuration file can be copied from /Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/newrelic.js and renamed to \"newrelic.js\" in the directory from which you will start your application. Attempting to start agent using environment variables."}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":5411,"time":"2024-05-15T09:15:10.326Z","msg":"Using New Relic for Node.js. Agent version: 11.16.0; Node version: v18.18.2."}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":5411,"time":"2024-05-15T09:15:10.420Z","msg":"Using AsyncLocalContextManager"}
|
|
||||||
{"v":0,"level":50,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":5411,"time":"2024-05-15T09:15:10.421Z","msg":"New Relic for Node.js was unable to bootstrap itself due to an error:","stack":"Error: New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!\n at createAgent (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:160:11)\n at initialize (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:105:15)\n at Object.<anonymous> (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:39:3)\n at Module._compile (node:internal/modules/cjs/loader:1256:14)\n at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)\n at Module.load (node:internal/modules/cjs/loader:1119:32)\n at Module._load (node:internal/modules/cjs/loader:960:12)\n at Module.require (node:internal/modules/cjs/loader:1143:19)\n at require (node:internal/modules/cjs/helpers:119:18)\n at Object.newrelic (/Users/ahmedbouhuolia/repos/bigcapital/packages/server/build/index.js:15954:18)","message":"New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!"}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":8856,"time":"2024-05-15T11:47:03.324Z","msg":"Unable to find configuration file. If a configuration file is desired (common for non-containerized environments), a base configuration file can be copied from /Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/newrelic.js and renamed to \"newrelic.js\" in the directory from which you will start your application. Attempting to start agent using environment variables."}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":8856,"time":"2024-05-15T11:47:03.335Z","msg":"Using New Relic for Node.js. Agent version: 11.16.0; Node version: v18.18.2."}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":8856,"time":"2024-05-15T11:47:03.504Z","msg":"Using AsyncLocalContextManager"}
|
|
||||||
{"v":0,"level":50,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":8856,"time":"2024-05-15T11:47:03.505Z","msg":"New Relic for Node.js was unable to bootstrap itself due to an error:","stack":"Error: New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!\n at createAgent (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:160:11)\n at initialize (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:105:15)\n at Object.<anonymous> (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:39:3)\n at Module._compile (node:internal/modules/cjs/loader:1256:14)\n at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)\n at Module.load (node:internal/modules/cjs/loader:1119:32)\n at Module._load (node:internal/modules/cjs/loader:960:12)\n at Module.require (node:internal/modules/cjs/loader:1143:19)\n at require (node:internal/modules/cjs/helpers:119:18)\n at Object.newrelic (/Users/ahmedbouhuolia/repos/bigcapital/packages/server/build/index.js:15954:18)","message":"New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!"}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":12076,"time":"2024-05-15T14:30:36.984Z","msg":"Unable to find configuration file. If a configuration file is desired (common for non-containerized environments), a base configuration file can be copied from /Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/newrelic.js and renamed to \"newrelic.js\" in the directory from which you will start your application. Attempting to start agent using environment variables."}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":12076,"time":"2024-05-15T14:30:36.990Z","msg":"Using New Relic for Node.js. Agent version: 11.16.0; Node version: v18.18.2."}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":12076,"time":"2024-05-15T14:30:37.126Z","msg":"Using AsyncLocalContextManager"}
|
|
||||||
{"v":0,"level":50,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":12076,"time":"2024-05-15T14:30:37.127Z","msg":"New Relic for Node.js was unable to bootstrap itself due to an error:","stack":"Error: New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!\n at createAgent (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:160:11)\n at initialize (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:105:15)\n at Object.<anonymous> (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:39:3)\n at Module._compile (node:internal/modules/cjs/loader:1256:14)\n at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)\n at Module.load (node:internal/modules/cjs/loader:1119:32)\n at Module._load (node:internal/modules/cjs/loader:960:12)\n at Module.require (node:internal/modules/cjs/loader:1143:19)\n at require (node:internal/modules/cjs/helpers:119:18)\n at Object.newrelic (/Users/ahmedbouhuolia/repos/bigcapital/packages/server/build/index.js:15954:18)","message":"New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!"}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":29079,"time":"2024-05-17T10:24:22.393Z","msg":"Unable to find configuration file. If a configuration file is desired (common for non-containerized environments), a base configuration file can be copied from /Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/newrelic.js and renamed to \"newrelic.js\" in the directory from which you will start your application. Attempting to start agent using environment variables."}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":29079,"time":"2024-05-17T10:24:22.405Z","msg":"Using New Relic for Node.js. Agent version: 11.16.0; Node version: v18.18.2."}
|
|
||||||
{"v":0,"level":30,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":29079,"time":"2024-05-17T10:24:22.536Z","msg":"Using AsyncLocalContextManager"}
|
|
||||||
{"v":0,"level":50,"name":"newrelic","hostname":"Ahmeds-MacBook-Air.local","pid":29079,"time":"2024-05-17T10:24:22.537Z","msg":"New Relic for Node.js was unable to bootstrap itself due to an error:","stack":"Error: New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!\n at createAgent (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:160:11)\n at initialize (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:105:15)\n at Object.<anonymous> (/Users/ahmedbouhuolia/repos/bigcapital/node_modules/.pnpm/newrelic@11.16.0/node_modules/newrelic/index.js:39:3)\n at Module._compile (node:internal/modules/cjs/loader:1256:14)\n at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)\n at Module.load (node:internal/modules/cjs/loader:1119:32)\n at Module._load (node:internal/modules/cjs/loader:960:12)\n at Module.require (node:internal/modules/cjs/loader:1143:19)\n at require (node:internal/modules/cjs/helpers:119:18)\n at Object.newrelic (/Users/ahmedbouhuolia/repos/bigcapital/packages/server/build/index.js:15954:18)","message":"New Relic requires that you name this application!\nSet app_name in your newrelic.js or newrelic.cjs file or set environment variable\nNEW_RELIC_APP_NAME. Not starting!"}
|
|
||||||
@@ -20,7 +20,6 @@
|
|||||||
"bigcapital": "./bin/bigcapital.js"
|
"bigcapital": "./bin/bigcapital.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.576.0",
|
|
||||||
"@casl/ability": "^5.4.3",
|
"@casl/ability": "^5.4.3",
|
||||||
"@hapi/boom": "^7.4.3",
|
"@hapi/boom": "^7.4.3",
|
||||||
"@lemonsqueezy/lemonsqueezy.js": "^2.2.0",
|
"@lemonsqueezy/lemonsqueezy.js": "^2.2.0",
|
||||||
@@ -83,7 +82,6 @@
|
|||||||
"mustache": "^3.0.3",
|
"mustache": "^3.0.3",
|
||||||
"mysql": "^2.17.1",
|
"mysql": "^2.17.1",
|
||||||
"mysql2": "^1.6.5",
|
"mysql2": "^1.6.5",
|
||||||
"newrelic": "^11.15.0",
|
|
||||||
"node-cache": "^4.2.1",
|
"node-cache": "^4.2.1",
|
||||||
"nodemailer": "^6.3.0",
|
"nodemailer": "^6.3.0",
|
||||||
"nodemon": "^1.19.1",
|
"nodemon": "^1.19.1",
|
||||||
|
|||||||
@@ -244,7 +244,6 @@
|
|||||||
"account.field.active": "Active",
|
"account.field.active": "Active",
|
||||||
"account.field.currency": "Currency",
|
"account.field.currency": "Currency",
|
||||||
"account.field.balance": "Balance",
|
"account.field.balance": "Balance",
|
||||||
"account.field.bank_balance": "Bank Balance",
|
|
||||||
"account.field.parent_account": "Parent Account",
|
"account.field.parent_account": "Parent Account",
|
||||||
"account.field.created_at": "Created at",
|
"account.field.created_at": "Created at",
|
||||||
"item.field.type": "Item Type",
|
"item.field.type": "Item Type",
|
||||||
@@ -332,7 +331,7 @@
|
|||||||
"bill_payment.field.reference_no": "Reference No.",
|
"bill_payment.field.reference_no": "Reference No.",
|
||||||
"bill_payment.field.description": "Description",
|
"bill_payment.field.description": "Description",
|
||||||
"bill_payment.field.exchange_rate": "Exchange Rate",
|
"bill_payment.field.exchange_rate": "Exchange Rate",
|
||||||
"bill_payment.field.note": "Note",
|
"bill_payment.field.statement": "Statement",
|
||||||
"bill_payment.field.entries.bill": "Bill No.",
|
"bill_payment.field.entries.bill": "Bill No.",
|
||||||
"bill_payment.field.entries.payment_amount": "Payment Amount",
|
"bill_payment.field.entries.payment_amount": "Payment Amount",
|
||||||
"bill_payment.field.reference": "Reference No.",
|
"bill_payment.field.reference": "Reference No.",
|
||||||
@@ -432,7 +431,6 @@
|
|||||||
"vendor.field.created_at": "Created at",
|
"vendor.field.created_at": "Created at",
|
||||||
"vendor.field.balance": "Balance",
|
"vendor.field.balance": "Balance",
|
||||||
"vendor.field.status": "Status",
|
"vendor.field.status": "Status",
|
||||||
"vendor.field.note": "Note",
|
|
||||||
"vendor.field.currency": "Currency",
|
"vendor.field.currency": "Currency",
|
||||||
"vendor.field.status.active": "Active",
|
"vendor.field.status.active": "Active",
|
||||||
"vendor.field.status.inactive": "Inactive",
|
"vendor.field.status.inactive": "Inactive",
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
import { Service, Inject } from 'typedi';
|
|
||||||
import { Router, Response } from 'express';
|
|
||||||
import BaseController from '@/api/controllers/BaseController';
|
|
||||||
import { Request } from 'express-validator/src/base';
|
|
||||||
import { AttachmentsApplication } from '@/services/Attachments/AttachmentsApplication';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class AttachmentsController extends BaseController {
|
|
||||||
@Inject()
|
|
||||||
private attachmentsApplication: AttachmentsApplication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Router constructor.
|
|
||||||
*/
|
|
||||||
router() {
|
|
||||||
const router = Router();
|
|
||||||
|
|
||||||
router.delete('/:id', this.deleteAttachment.bind(this));
|
|
||||||
router.post('/', this.uploadAttachment.bind(this));
|
|
||||||
|
|
||||||
return router;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async uploadAttachment(req: Request, res: Response, next: Function) {
|
|
||||||
try {
|
|
||||||
await this.attachmentsApplication.upload();
|
|
||||||
|
|
||||||
return res.status(200).send({});
|
|
||||||
} catch (error) {
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private deleteAttachment(req: Request, res: Response, next: Function) {}
|
|
||||||
}
|
|
||||||
@@ -9,8 +9,6 @@ import { DATATYPES_LENGTH } from '@/data/DataTypes';
|
|||||||
import LoginThrottlerMiddleware from '@/api/middleware/LoginThrottlerMiddleware';
|
import LoginThrottlerMiddleware from '@/api/middleware/LoginThrottlerMiddleware';
|
||||||
import AuthenticationApplication from '@/services/Authentication/AuthApplication';
|
import AuthenticationApplication from '@/services/Authentication/AuthApplication';
|
||||||
|
|
||||||
import JWTAuth from '@/api/middleware/jwtAuth';
|
|
||||||
import AttachCurrentTenantUser from '@/api/middleware/AttachCurrentTenantUser';
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class AuthenticationController extends BaseController {
|
export default class AuthenticationController extends BaseController {
|
||||||
@Inject()
|
@Inject()
|
||||||
@@ -30,20 +28,6 @@ export default class AuthenticationController extends BaseController {
|
|||||||
asyncMiddleware(this.login.bind(this)),
|
asyncMiddleware(this.login.bind(this)),
|
||||||
this.handlerErrors
|
this.handlerErrors
|
||||||
);
|
);
|
||||||
router.use('/register/verify/resend', JWTAuth);
|
|
||||||
router.use('/register/verify/resend', AttachCurrentTenantUser);
|
|
||||||
router.post(
|
|
||||||
'/register/verify/resend',
|
|
||||||
asyncMiddleware(this.registerVerifyResendMail.bind(this)),
|
|
||||||
this.handlerErrors
|
|
||||||
);
|
|
||||||
router.post(
|
|
||||||
'/register/verify',
|
|
||||||
this.signupVerifySchema,
|
|
||||||
this.validationResult,
|
|
||||||
asyncMiddleware(this.registerVerify.bind(this)),
|
|
||||||
this.handlerErrors
|
|
||||||
);
|
|
||||||
router.post(
|
router.post(
|
||||||
'/register',
|
'/register',
|
||||||
this.registerSchema,
|
this.registerSchema,
|
||||||
@@ -115,17 +99,6 @@ export default class AuthenticationController extends BaseController {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private get signupVerifySchema(): ValidationChain[] {
|
|
||||||
return [
|
|
||||||
check('email')
|
|
||||||
.exists()
|
|
||||||
.isString()
|
|
||||||
.isEmail()
|
|
||||||
.isLength({ max: DATATYPES_LENGTH.STRING }),
|
|
||||||
check('token').exists().isString(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset password schema.
|
* Reset password schema.
|
||||||
* @returns {ValidationChain[]}
|
* @returns {ValidationChain[]}
|
||||||
@@ -193,58 +166,6 @@ export default class AuthenticationController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies the provider user's email after signin-up.
|
|
||||||
* @param {Request} req
|
|
||||||
* @param {Response}| res
|
|
||||||
* @param {Function} next
|
|
||||||
* @returns {Response|void}
|
|
||||||
*/
|
|
||||||
private async registerVerify(req: Request, res: Response, next: Function) {
|
|
||||||
const signUpVerifyDTO: { email: string; token: string } =
|
|
||||||
this.matchedBodyData(req);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const user = await this.authApplication.signUpConfirm(
|
|
||||||
signUpVerifyDTO.email,
|
|
||||||
signUpVerifyDTO.token
|
|
||||||
);
|
|
||||||
return res.status(200).send({
|
|
||||||
type: 'success',
|
|
||||||
message: 'The given user has verified successfully',
|
|
||||||
user,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resends the confirmation email to the user.
|
|
||||||
* @param {Request} req
|
|
||||||
* @param {Response}| res
|
|
||||||
* @param {Function} next
|
|
||||||
*/
|
|
||||||
private async registerVerifyResendMail(
|
|
||||||
req: Request,
|
|
||||||
res: Response,
|
|
||||||
next: Function
|
|
||||||
) {
|
|
||||||
const { user } = req;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const data = await this.authApplication.signUpConfirmResend(user.id);
|
|
||||||
|
|
||||||
return res.status(200).send({
|
|
||||||
type: 'success',
|
|
||||||
message: 'The given user has verified successfully',
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send reset password handler
|
* Send reset password handler
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { Router, Request, Response, NextFunction } from 'express';
|
|
||||||
import { query } from 'express-validator';
|
|
||||||
import BaseController from '@/api/controllers/BaseController';
|
|
||||||
import { ServiceError } from '@/exceptions';
|
|
||||||
import { ExportApplication } from '@/services/Export/ExportApplication';
|
|
||||||
import { ACCEPT_TYPE } from '@/interfaces/Http';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class ExportController extends BaseController {
|
|
||||||
@Inject()
|
|
||||||
private exportResourceApp: ExportApplication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Router constructor method.
|
|
||||||
*/
|
|
||||||
router() {
|
|
||||||
const router = Router();
|
|
||||||
|
|
||||||
router.get(
|
|
||||||
'/',
|
|
||||||
[
|
|
||||||
query('resource').exists(),
|
|
||||||
query('format').isIn(['csv', 'xlsx']).optional(),
|
|
||||||
],
|
|
||||||
this.validationResult,
|
|
||||||
this.export.bind(this),
|
|
||||||
this.catchServiceErrors
|
|
||||||
);
|
|
||||||
return router;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Imports xlsx/csv to the given resource type.
|
|
||||||
* @param {Request} req -
|
|
||||||
* @param {Response} res -
|
|
||||||
* @param {NextFunction} next -
|
|
||||||
*/
|
|
||||||
private async export(req: Request, res: Response, next: NextFunction) {
|
|
||||||
const { tenantId } = req;
|
|
||||||
const query = this.matchedQueryData(req);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const accept = this.accepts(req);
|
|
||||||
|
|
||||||
const acceptType = accept.types([
|
|
||||||
ACCEPT_TYPE.APPLICATION_XLSX,
|
|
||||||
ACCEPT_TYPE.APPLICATION_CSV,
|
|
||||||
ACCEPT_TYPE.APPLICATION_PDF,
|
|
||||||
]);
|
|
||||||
const data = await this.exportResourceApp.export(
|
|
||||||
tenantId,
|
|
||||||
query.resource,
|
|
||||||
acceptType === ACCEPT_TYPE.APPLICATION_XLSX ? 'xlsx' : 'csv'
|
|
||||||
);
|
|
||||||
if (ACCEPT_TYPE.APPLICATION_CSV === acceptType) {
|
|
||||||
res.setHeader('Content-Disposition', 'attachment; filename=output.csv');
|
|
||||||
res.setHeader('Content-Type', 'text/csv');
|
|
||||||
|
|
||||||
return res.send(data);
|
|
||||||
// Retrieves the xlsx format.
|
|
||||||
} else if (ACCEPT_TYPE.APPLICATION_XLSX === acceptType) {
|
|
||||||
res.setHeader(
|
|
||||||
'Content-Disposition',
|
|
||||||
'attachment; filename=output.xlsx'
|
|
||||||
);
|
|
||||||
res.setHeader(
|
|
||||||
'Content-Type',
|
|
||||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
||||||
);
|
|
||||||
return res.send(data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms service errors to response.
|
|
||||||
* @param {Error}
|
|
||||||
* @param {Request} req
|
|
||||||
* @param {Response} res
|
|
||||||
* @param {ServiceError} error
|
|
||||||
*/
|
|
||||||
private catchServiceErrors(
|
|
||||||
error,
|
|
||||||
req: Request,
|
|
||||||
res: Response,
|
|
||||||
next: NextFunction
|
|
||||||
) {
|
|
||||||
if (error instanceof ServiceError) {
|
|
||||||
return res.status(400).send({
|
|
||||||
errors: [{ type: error.errorType }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -61,7 +61,6 @@ import { TaxRatesController } from './controllers/TaxRates/TaxRates';
|
|||||||
import { ImportController } from './controllers/Import/ImportController';
|
import { ImportController } from './controllers/Import/ImportController';
|
||||||
import { BankingController } from './controllers/Banking/BankingController';
|
import { BankingController } from './controllers/Banking/BankingController';
|
||||||
import { Webhooks } from './controllers/Webhooks/Webhooks';
|
import { Webhooks } from './controllers/Webhooks/Webhooks';
|
||||||
import { ExportController } from './controllers/Export/ExportController';
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const app = Router();
|
const app = Router();
|
||||||
@@ -142,7 +141,6 @@ export default () => {
|
|||||||
dashboard.use('/projects', Container.get(ProjectsController).router());
|
dashboard.use('/projects', Container.get(ProjectsController).router());
|
||||||
dashboard.use('/tax-rates', Container.get(TaxRatesController).router());
|
dashboard.use('/tax-rates', Container.get(TaxRatesController).router());
|
||||||
dashboard.use('/import', Container.get(ImportController).router());
|
dashboard.use('/import', Container.get(ImportController).router());
|
||||||
dashboard.use('/export', Container.get(ExportController).router())
|
|
||||||
|
|
||||||
dashboard.use('/', Container.get(ProjectTasksController).router());
|
dashboard.use('/', Container.get(ProjectTasksController).router());
|
||||||
dashboard.use('/', Container.get(ProjectTimesController).router());
|
dashboard.use('/', Container.get(ProjectTimesController).router());
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ module.exports = {
|
|||||||
mail: {
|
mail: {
|
||||||
host: process.env.MAIL_HOST,
|
host: process.env.MAIL_HOST,
|
||||||
port: process.env.MAIL_PORT,
|
port: process.env.MAIL_PORT,
|
||||||
secure: parseBoolean(defaultTo(process.env.MAIL_SECURE, false), false),
|
secure: !!parseInt(process.env.MAIL_SECURE, 10),
|
||||||
username: process.env.MAIL_USERNAME,
|
username: process.env.MAIL_USERNAME,
|
||||||
password: process.env.MAIL_PASSWORD,
|
password: process.env.MAIL_PASSWORD,
|
||||||
from: process.env.MAIL_FROM_ADDRESS,
|
from: process.env.MAIL_FROM_ADDRESS,
|
||||||
@@ -153,13 +153,6 @@ module.exports = {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign-up email confirmation
|
|
||||||
*/
|
|
||||||
signupConfirmation: {
|
|
||||||
enabled: parseBoolean<boolean>(process.env.SIGNUP_EMAIL_CONFIRMATION, false),
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puppeteer remote browserless connection.
|
* Puppeteer remote browserless connection.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -66,27 +66,16 @@ export interface IAuthResetedPasswordEventPayload {
|
|||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface IAuthSendingResetPassword {
|
export interface IAuthSendingResetPassword {
|
||||||
user: ISystemUser;
|
user: ISystemUser,
|
||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
export interface IAuthSendedResetPassword {
|
export interface IAuthSendedResetPassword {
|
||||||
user: ISystemUser;
|
user: ISystemUser,
|
||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IAuthGetMetaPOJO {
|
export interface IAuthGetMetaPOJO {
|
||||||
signupDisabled: boolean;
|
signupDisabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IAuthSignUpVerifingEventPayload {
|
|
||||||
email: string;
|
|
||||||
verifyToken: string;
|
|
||||||
userId: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IAuthSignUpVerifiedEventPayload {
|
|
||||||
email: string;
|
|
||||||
verifyToken: string;
|
|
||||||
userId: number;
|
|
||||||
}
|
|
||||||
@@ -126,16 +126,13 @@ export interface IModelMeta {
|
|||||||
defaultFilterField: string;
|
defaultFilterField: string;
|
||||||
defaultSort: IModelMetaDefaultSort;
|
defaultSort: IModelMetaDefaultSort;
|
||||||
|
|
||||||
exportable?: boolean;
|
|
||||||
exportFlattenOn?: string;
|
|
||||||
|
|
||||||
importable?: boolean;
|
importable?: boolean;
|
||||||
|
|
||||||
importAggregator?: string;
|
importAggregator?: string;
|
||||||
importAggregateOn?: string;
|
importAggregateOn?: string;
|
||||||
importAggregateBy?: string;
|
importAggregateBy?: string;
|
||||||
|
|
||||||
fields: { [key: string]: IModelMetaField };
|
fields: { [key: string]: IModelMetaField };
|
||||||
columns: { [key: string]: IModelMetaColumn };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
@@ -164,22 +161,3 @@ export type IModelMetaField2 = IModelMetaFieldCommon2 &
|
|||||||
| IModelMetaRelationField2
|
| IModelMetaRelationField2
|
||||||
| IModelMetaCollectionField
|
| IModelMetaCollectionField
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface ImodelMetaColumnMeta {
|
|
||||||
name: string;
|
|
||||||
accessor?: string;
|
|
||||||
exportable?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IModelMetaColumnText {
|
|
||||||
type: 'text;';
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IModelMetaColumnCollection {
|
|
||||||
type: 'collection';
|
|
||||||
collectionOf: 'object';
|
|
||||||
columns: { [key: string]: ImodelMetaColumnMeta & IModelMetaColumnText };
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IModelMetaColumn = ImodelMetaColumnMeta &
|
|
||||||
(IModelMetaColumnText | IModelMetaColumnCollection);
|
|
||||||
|
|||||||
@@ -91,7 +91,6 @@ import { SaleEstimateMarkApprovedOnMailSent } from '@/services/Sales/Estimates/s
|
|||||||
import { DeleteCashflowTransactionOnUncategorize } from '@/services/Cashflow/subscribers/DeleteCashflowTransactionOnUncategorize';
|
import { DeleteCashflowTransactionOnUncategorize } from '@/services/Cashflow/subscribers/DeleteCashflowTransactionOnUncategorize';
|
||||||
import { PreventDeleteTransactionOnDelete } from '@/services/Cashflow/subscribers/PreventDeleteTransactionsOnDelete';
|
import { PreventDeleteTransactionOnDelete } from '@/services/Cashflow/subscribers/PreventDeleteTransactionsOnDelete';
|
||||||
import { SubscribeFreeOnSignupCommunity } from '@/services/Subscription/events/SubscribeFreeOnSignupCommunity';
|
import { SubscribeFreeOnSignupCommunity } from '@/services/Subscription/events/SubscribeFreeOnSignupCommunity';
|
||||||
import { SendVerfiyMailOnSignUp } from '@/services/Authentication/events/SendVerfiyMailOnSignUp';
|
|
||||||
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
@@ -223,7 +222,6 @@ export const susbcribers = () => {
|
|||||||
DeleteCashflowTransactionOnUncategorize,
|
DeleteCashflowTransactionOnUncategorize,
|
||||||
PreventDeleteTransactionOnDelete,
|
PreventDeleteTransactionOnDelete,
|
||||||
|
|
||||||
SubscribeFreeOnSignupCommunity,
|
SubscribeFreeOnSignupCommunity
|
||||||
SendVerfiyMailOnSignUp
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import { SaleReceiptMailNotificationJob } from '@/services/Sales/Receipts/SaleRe
|
|||||||
import { PaymentReceiveMailNotificationJob } from '@/services/Sales/PaymentReceives/PaymentReceiveMailNotificationJob';
|
import { PaymentReceiveMailNotificationJob } from '@/services/Sales/PaymentReceives/PaymentReceiveMailNotificationJob';
|
||||||
import { PlaidFetchTransactionsJob } from '@/services/Banking/Plaid/PlaidFetchTransactionsJob';
|
import { PlaidFetchTransactionsJob } from '@/services/Banking/Plaid/PlaidFetchTransactionsJob';
|
||||||
import { ImportDeleteExpiredFilesJobs } from '@/services/Import/jobs/ImportDeleteExpiredFilesJob';
|
import { ImportDeleteExpiredFilesJobs } from '@/services/Import/jobs/ImportDeleteExpiredFilesJob';
|
||||||
import { SendVerifyMailJob } from '@/services/Authentication/jobs/SendVerifyMailJob';
|
|
||||||
|
|
||||||
export default ({ agenda }: { agenda: Agenda }) => {
|
export default ({ agenda }: { agenda: Agenda }) => {
|
||||||
new ResetPasswordMailJob(agenda);
|
new ResetPasswordMailJob(agenda);
|
||||||
@@ -28,7 +27,6 @@ export default ({ agenda }: { agenda: Agenda }) => {
|
|||||||
new PaymentReceiveMailNotificationJob(agenda);
|
new PaymentReceiveMailNotificationJob(agenda);
|
||||||
new PlaidFetchTransactionsJob(agenda);
|
new PlaidFetchTransactionsJob(agenda);
|
||||||
new ImportDeleteExpiredFilesJobs(agenda);
|
new ImportDeleteExpiredFilesJobs(agenda);
|
||||||
new SendVerifyMailJob(agenda);
|
|
||||||
|
|
||||||
agenda.start().then(() => {
|
agenda.start().then(() => {
|
||||||
agenda.every('1 hours', 'delete-expired-imported-files', {});
|
agenda.every('1 hours', 'delete-expired-imported-files', {});
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ export default {
|
|||||||
sortField: 'name',
|
sortField: 'name',
|
||||||
},
|
},
|
||||||
importable: true,
|
importable: true,
|
||||||
exportable: true,
|
|
||||||
fields: {
|
fields: {
|
||||||
name: {
|
name: {
|
||||||
name: 'account.field.name',
|
name: 'account.field.name',
|
||||||
@@ -86,55 +85,6 @@ export default {
|
|||||||
fieldType: 'date',
|
fieldType: 'date',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
name: {
|
|
||||||
name: 'account.field.name',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
code: {
|
|
||||||
name: 'account.field.code',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
rootType: {
|
|
||||||
name: 'account.field.root_type',
|
|
||||||
type: 'text',
|
|
||||||
accessor: 'accountRootType',
|
|
||||||
},
|
|
||||||
accountType: {
|
|
||||||
name: 'account.field.type',
|
|
||||||
accessor: 'accountTypeLabel',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
accountNormal: {
|
|
||||||
name: 'account.field.normal',
|
|
||||||
accessor: 'accountNormalFormatted',
|
|
||||||
},
|
|
||||||
currencyCode: {
|
|
||||||
name: 'account.field.currency',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
bankBalance: {
|
|
||||||
name: 'account.field.bank_balance',
|
|
||||||
accessor: 'bankBalanceFormatted',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
balance: {
|
|
||||||
name: 'account.field.balance',
|
|
||||||
accessor: 'amount',
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
name: 'account.field.description',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
active: {
|
|
||||||
name: 'account.field.active',
|
|
||||||
type: 'boolean',
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
name: 'account.field.created_at',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields2: {
|
fields2: {
|
||||||
name: {
|
name: {
|
||||||
name: 'account.field.name',
|
name: 'account.field.name',
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ export default {
|
|||||||
sortField: 'bill_date',
|
sortField: 'bill_date',
|
||||||
},
|
},
|
||||||
importable: true,
|
importable: true,
|
||||||
exportFlattenOn: 'entries',
|
|
||||||
exportable: true,
|
|
||||||
importAggregator: 'group',
|
importAggregator: 'group',
|
||||||
importAggregateOn: 'entries',
|
importAggregateOn: 'entries',
|
||||||
importAggregateBy: 'billNumber',
|
importAggregateBy: 'billNumber',
|
||||||
@@ -82,84 +80,6 @@ export default {
|
|||||||
fieldType: 'date',
|
fieldType: 'date',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
billNumber: {
|
|
||||||
name: 'Bill No.',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
referenceNo: {
|
|
||||||
name: 'Reference No.',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
billDate: {
|
|
||||||
name: 'Date',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
dueDate: {
|
|
||||||
name: 'Due Date',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
vendorId: {
|
|
||||||
name: 'Vendor',
|
|
||||||
accessor: 'vendor.displayName',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
name: 'Amount',
|
|
||||||
accessor: 'formattedAmount',
|
|
||||||
},
|
|
||||||
exchangeRate: {
|
|
||||||
name: 'Exchange Rate',
|
|
||||||
type: 'number',
|
|
||||||
},
|
|
||||||
currencyCode: {
|
|
||||||
name: 'Currency Code',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
dueAmount: {
|
|
||||||
name: 'Due Amount',
|
|
||||||
accessor: 'formattedDueAmount',
|
|
||||||
},
|
|
||||||
paidAmount: {
|
|
||||||
name: 'Paid Amount',
|
|
||||||
accessor: 'formattedPaymentAmount',
|
|
||||||
},
|
|
||||||
note: {
|
|
||||||
name: 'Note',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
open: {
|
|
||||||
name: 'Open',
|
|
||||||
type: 'boolean',
|
|
||||||
},
|
|
||||||
entries: {
|
|
||||||
name: 'Entries',
|
|
||||||
accessor: 'entries',
|
|
||||||
type: 'collection',
|
|
||||||
collectionOf: 'object',
|
|
||||||
columns: {
|
|
||||||
itemName: {
|
|
||||||
name: 'Item Name',
|
|
||||||
accessor: 'item.name',
|
|
||||||
},
|
|
||||||
rate: {
|
|
||||||
name: 'Item Rate',
|
|
||||||
accessor: 'rateFormatted',
|
|
||||||
},
|
|
||||||
quantity: {
|
|
||||||
name: 'Item Quantity',
|
|
||||||
accessor: 'quantityFormatted',
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
name: 'Item Description',
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
name: 'Item Amount',
|
|
||||||
accessor: 'totalFormatted',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields2: {
|
fields2: {
|
||||||
billNumber: {
|
billNumber: {
|
||||||
name: 'Bill No.',
|
name: 'Bill No.',
|
||||||
@@ -212,7 +132,7 @@ export default {
|
|||||||
relationModel: 'Item',
|
relationModel: 'Item',
|
||||||
relationImportMatch: ['name', 'code'],
|
relationImportMatch: ['name', 'code'],
|
||||||
required: true,
|
required: true,
|
||||||
importHint: 'Matches the item name or code.',
|
importHint: "Matches the item name or code."
|
||||||
},
|
},
|
||||||
rate: {
|
rate: {
|
||||||
name: 'Rate',
|
name: 'Rate',
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ export default {
|
|||||||
sortOrder: 'DESC',
|
sortOrder: 'DESC',
|
||||||
sortField: 'bill_date',
|
sortField: 'bill_date',
|
||||||
},
|
},
|
||||||
exportable: true,
|
|
||||||
importable: true,
|
importable: true,
|
||||||
importAggregator: 'group',
|
importAggregator: 'group',
|
||||||
importAggregateOn: 'entries',
|
importAggregateOn: 'entries',
|
||||||
@@ -68,46 +67,6 @@ export default {
|
|||||||
fieldType: 'date',
|
fieldType: 'date',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
vendor: {
|
|
||||||
name: 'bill_payment.field.vendor',
|
|
||||||
type: 'relation',
|
|
||||||
accessor: 'vendor.displayName',
|
|
||||||
},
|
|
||||||
paymentDate: {
|
|
||||||
name: 'bill_payment.field.payment_date',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
paymentNumber: {
|
|
||||||
name: 'bill_payment.field.payment_number',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
paymentAccount: {
|
|
||||||
name: 'bill_payment.field.payment_account',
|
|
||||||
accessor: 'paymentAccount.name',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
name: 'Amount',
|
|
||||||
accessor: 'formattedAmount',
|
|
||||||
},
|
|
||||||
currencyCode: {
|
|
||||||
name: 'Currency Code',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
exchangeRate: {
|
|
||||||
name: 'bill_payment.field.exchange_rate',
|
|
||||||
type: 'number',
|
|
||||||
},
|
|
||||||
statement: {
|
|
||||||
name: 'bill_payment.field.note',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
reference: {
|
|
||||||
name: 'bill_payment.field.reference',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields2: {
|
fields2: {
|
||||||
vendorId: {
|
vendorId: {
|
||||||
name: 'bill_payment.field.vendor',
|
name: 'bill_payment.field.vendor',
|
||||||
@@ -125,7 +84,7 @@ export default {
|
|||||||
name: 'bill_payment.field.payment_number',
|
name: 'bill_payment.field.payment_number',
|
||||||
fieldType: 'text',
|
fieldType: 'text',
|
||||||
unique: true,
|
unique: true,
|
||||||
importHint: 'The payment number should be unique.',
|
importHint: "The payment number should be unique."
|
||||||
},
|
},
|
||||||
paymentAccountId: {
|
paymentAccountId: {
|
||||||
name: 'bill_payment.field.payment_account',
|
name: 'bill_payment.field.payment_account',
|
||||||
@@ -133,14 +92,14 @@ export default {
|
|||||||
relationModel: 'Account',
|
relationModel: 'Account',
|
||||||
relationImportMatch: ['name', 'code'],
|
relationImportMatch: ['name', 'code'],
|
||||||
required: true,
|
required: true,
|
||||||
importHint: 'Matches the account name or code.',
|
importHint: "Matches the account name or code."
|
||||||
},
|
},
|
||||||
exchangeRate: {
|
exchangeRate: {
|
||||||
name: 'bill_payment.field.exchange_rate',
|
name: 'bill_payment.field.exchange_rate',
|
||||||
fieldType: 'number',
|
fieldType: 'number',
|
||||||
},
|
},
|
||||||
statement: {
|
statement: {
|
||||||
name: 'bill_payment.field.note',
|
name: 'bill_payment.field.statement',
|
||||||
fieldType: 'text',
|
fieldType: 'text',
|
||||||
},
|
},
|
||||||
reference: {
|
reference: {
|
||||||
@@ -161,7 +120,7 @@ export default {
|
|||||||
relationModel: 'Bill',
|
relationModel: 'Bill',
|
||||||
relationImportMatch: 'billNumber',
|
relationImportMatch: 'billNumber',
|
||||||
required: true,
|
required: true,
|
||||||
importHint: 'Matches the bill number.',
|
importHint: "Matches the bill number."
|
||||||
},
|
},
|
||||||
paymentAmount: {
|
paymentAmount: {
|
||||||
name: 'bill_payment.field.entries.payment_amount',
|
name: 'bill_payment.field.entries.payment_amount',
|
||||||
|
|||||||
@@ -12,14 +12,10 @@ export default {
|
|||||||
sortOrder: 'DESC',
|
sortOrder: 'DESC',
|
||||||
sortField: 'name',
|
sortField: 'name',
|
||||||
},
|
},
|
||||||
exportable: true,
|
|
||||||
exportFlattenOn: 'entries',
|
|
||||||
|
|
||||||
importable: true,
|
importable: true,
|
||||||
importAggregator: 'group',
|
importAggregator: 'group',
|
||||||
importAggregateOn: 'entries',
|
importAggregateOn: 'entries',
|
||||||
importAggregateBy: 'creditNoteNumber',
|
importAggregateBy: 'creditNoteNumber',
|
||||||
|
|
||||||
fields: {
|
fields: {
|
||||||
customer: {
|
customer: {
|
||||||
name: 'credit_note.field.customer',
|
name: 'credit_note.field.customer',
|
||||||
@@ -85,67 +81,6 @@ export default {
|
|||||||
fieldType: 'date',
|
fieldType: 'date',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
customer: {
|
|
||||||
name: 'Customer',
|
|
||||||
type: 'relation',
|
|
||||||
accessor: 'customer.displayName',
|
|
||||||
},
|
|
||||||
exchangeRate: {
|
|
||||||
name: 'Exchange Rate',
|
|
||||||
type: 'number',
|
|
||||||
},
|
|
||||||
creditNoteDate: {
|
|
||||||
name: 'Credit Note Date',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
referenceNo: {
|
|
||||||
name: 'Reference No.',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
note: {
|
|
||||||
name: 'Note',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
termsConditions: {
|
|
||||||
name: 'Terms & Conditions',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
creditNoteNumber: {
|
|
||||||
name: 'Credit Note Number',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
open: {
|
|
||||||
name: 'Open',
|
|
||||||
type: 'boolean',
|
|
||||||
},
|
|
||||||
entries: {
|
|
||||||
name: 'Entries',
|
|
||||||
type: 'collection',
|
|
||||||
collectionOf: 'object',
|
|
||||||
columns: {
|
|
||||||
itemName: {
|
|
||||||
name: 'Item Name',
|
|
||||||
accessor: 'item.name',
|
|
||||||
},
|
|
||||||
rate: {
|
|
||||||
name: 'Item Rate',
|
|
||||||
accessor: 'rateFormatted',
|
|
||||||
},
|
|
||||||
quantity: {
|
|
||||||
name: 'Item Quantity',
|
|
||||||
accessor: 'quantityFormatted',
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
name: 'Item Description',
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
name: 'Item Amount',
|
|
||||||
accessor: 'totalFormatted',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields2: {
|
fields2: {
|
||||||
customerId: {
|
customerId: {
|
||||||
name: 'Customer',
|
name: 'Customer',
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
export default {
|
export default {
|
||||||
importable: true,
|
importable: true,
|
||||||
exportable: true,
|
|
||||||
defaultFilterField: 'displayName',
|
defaultFilterField: 'displayName',
|
||||||
defaultSort: {
|
defaultSort: {
|
||||||
sortOrder: 'DESC',
|
sortOrder: 'DESC',
|
||||||
@@ -91,138 +90,6 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
firstName: {
|
|
||||||
name: 'vendor.field.first_name',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
lastName: {
|
|
||||||
name: 'vendor.field.last_name',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
displayName: {
|
|
||||||
name: 'vendor.field.display_name',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
email: {
|
|
||||||
name: 'vendor.field.email',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
workPhone: {
|
|
||||||
name: 'vendor.field.work_phone',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
personalPhone: {
|
|
||||||
name: 'vendor.field.personal_phone',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
companyName: {
|
|
||||||
name: 'vendor.field.company_name',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
website: {
|
|
||||||
name: 'vendor.field.website',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
balance: {
|
|
||||||
name: 'vendor.field.balance',
|
|
||||||
type: 'number',
|
|
||||||
},
|
|
||||||
openingBalance: {
|
|
||||||
name: 'vendor.field.opening_balance',
|
|
||||||
type: 'number',
|
|
||||||
},
|
|
||||||
openingBalanceAt: {
|
|
||||||
name: 'vendor.field.opening_balance_at',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
currencyCode: {
|
|
||||||
name: 'vendor.field.currency',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
name: 'vendor.field.status',
|
|
||||||
},
|
|
||||||
note: {
|
|
||||||
name: 'vendor.field.note',
|
|
||||||
},
|
|
||||||
// Billing Address
|
|
||||||
billingAddress1: {
|
|
||||||
name: 'Billing Address 1',
|
|
||||||
column: 'billing_address1',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
billingAddress2: {
|
|
||||||
name: 'Billing Address 2',
|
|
||||||
column: 'billing_address2',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
billingAddressCity: {
|
|
||||||
name: 'Billing Address City',
|
|
||||||
column: 'billing_address_city',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
billingAddressCountry: {
|
|
||||||
name: 'Billing Address Country',
|
|
||||||
column: 'billing_address_country',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
billingAddressPostcode: {
|
|
||||||
name: 'Billing Address Postcode',
|
|
||||||
column: 'billing_address_postcode',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
billingAddressState: {
|
|
||||||
name: 'Billing Address State',
|
|
||||||
column: 'billing_address_state',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
billingAddressPhone: {
|
|
||||||
name: 'Billing Address Phone',
|
|
||||||
column: 'billing_address_phone',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
// Shipping Address
|
|
||||||
shippingAddress1: {
|
|
||||||
name: 'Shipping Address 1',
|
|
||||||
column: 'shipping_address1',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
shippingAddress2: {
|
|
||||||
name: 'Shipping Address 2',
|
|
||||||
column: 'shipping_address2',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
shippingAddressCity: {
|
|
||||||
name: 'Shipping Address City',
|
|
||||||
column: 'shipping_address_city',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
shippingAddressCountry: {
|
|
||||||
name: 'Shipping Address Country',
|
|
||||||
column: 'shipping_address_country',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
shippingAddressPostcode: {
|
|
||||||
name: 'Shipping Address Postcode',
|
|
||||||
column: 'shipping_address_postcode',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
shippingAddressPhone: {
|
|
||||||
name: 'Shipping Address Phone',
|
|
||||||
column: 'shipping_address_phone',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
shippingAddressState: {
|
|
||||||
name: 'Shipping Address State',
|
|
||||||
column: 'shipping_address_state',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
name: 'vendor.field.created_at',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields2: {
|
fields2: {
|
||||||
customerType: {
|
customerType: {
|
||||||
name: 'Customer Type',
|
name: 'Customer Type',
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ export default {
|
|||||||
sortField: 'name',
|
sortField: 'name',
|
||||||
},
|
},
|
||||||
importable: true,
|
importable: true,
|
||||||
exportFlattenOn: 'categories',
|
|
||||||
exportable: true,
|
|
||||||
fields: {
|
fields: {
|
||||||
payment_date: {
|
payment_date: {
|
||||||
name: 'expense.field.payment_date',
|
name: 'expense.field.payment_date',
|
||||||
@@ -63,56 +61,6 @@ export default {
|
|||||||
fieldType: 'date',
|
fieldType: 'date',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
paymentReceive: {
|
|
||||||
name: 'expense.field.payment_account',
|
|
||||||
type: 'text',
|
|
||||||
accessor: 'paymentAccount.name'
|
|
||||||
},
|
|
||||||
referenceNo: {
|
|
||||||
name: 'expense.field.reference_no',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
paymentDate: {
|
|
||||||
name: 'expense.field.payment_date',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
currencyCode: {
|
|
||||||
name: 'expense.field.currency_code',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
exchangeRate: {
|
|
||||||
name: 'expense.field.exchange_rate',
|
|
||||||
type: 'number',
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
name: 'expense.field.description',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
categories: {
|
|
||||||
name: 'expense.field.categories',
|
|
||||||
type: 'collection',
|
|
||||||
collectionOf: 'object',
|
|
||||||
columns: {
|
|
||||||
expenseAccount: {
|
|
||||||
name: 'expense.field.expense_account',
|
|
||||||
accessor: 'expenseAccount.name',
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
name: 'expense.field.amount',
|
|
||||||
accessor: 'amountFormatted',
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
name: 'expense.field.line_description',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
publish: {
|
|
||||||
name: 'expense.field.publish',
|
|
||||||
type: 'boolean',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields2: {
|
fields2: {
|
||||||
paymentAccountId: {
|
paymentAccountId: {
|
||||||
name: 'expense.field.payment_account',
|
name: 'expense.field.payment_account',
|
||||||
@@ -120,7 +68,7 @@ export default {
|
|||||||
relationModel: 'Account',
|
relationModel: 'Account',
|
||||||
relationImportMatch: ['name', 'code'],
|
relationImportMatch: ['name', 'code'],
|
||||||
required: true,
|
required: true,
|
||||||
importHint: 'Matches the account name or code.',
|
importHint: "Matches the account name or code."
|
||||||
},
|
},
|
||||||
referenceNo: {
|
referenceNo: {
|
||||||
name: 'expense.field.reference_no',
|
name: 'expense.field.reference_no',
|
||||||
@@ -154,7 +102,7 @@ export default {
|
|||||||
relationModel: 'Account',
|
relationModel: 'Account',
|
||||||
relationImportMatch: ['name', 'code'],
|
relationImportMatch: ['name', 'code'],
|
||||||
required: true,
|
required: true,
|
||||||
importHint: 'Matches the account name or code.',
|
importHint: "Matches the account name or code."
|
||||||
},
|
},
|
||||||
amount: {
|
amount: {
|
||||||
name: 'expense.field.amount',
|
name: 'expense.field.amount',
|
||||||
|
|||||||
@@ -4,54 +4,6 @@ export default {
|
|||||||
sortOrder: 'DESC',
|
sortOrder: 'DESC',
|
||||||
sortField: 'date',
|
sortField: 'date',
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
date: {
|
|
||||||
name: 'inventory_adjustment.field.date',
|
|
||||||
column: 'date',
|
|
||||||
fieldType: 'date',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
name: 'inventory_adjustment.field.type',
|
|
||||||
column: 'type',
|
|
||||||
fieldType: 'enumeration',
|
|
||||||
options: [
|
|
||||||
{ key: 'increment', name: 'inventory_adjustment.field.type.increment' },
|
|
||||||
{ key: 'decrement', name: 'inventory_adjustment.field.type.decrement' },
|
|
||||||
],
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
adjustmentAccount: {
|
|
||||||
name: 'inventory_adjustment.field.adjustment_account',
|
|
||||||
type: 'adjustment_account_id',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
reason: {
|
|
||||||
name: 'inventory_adjustment.field.reason',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
referenceNo: {
|
|
||||||
name: 'inventory_adjustment.field.reference_no',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
name: 'inventory_adjustment.field.description',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
publishedAt: {
|
|
||||||
name: 'inventory_adjustment.field.published_at',
|
|
||||||
type: 'date',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
name: 'inventory_adjustment.field.created_at',
|
|
||||||
type: 'date',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields: {
|
fields: {
|
||||||
date: {
|
date: {
|
||||||
name: 'inventory_adjustment.field.date',
|
name: 'inventory_adjustment.field.date',
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
export default {
|
export default {
|
||||||
importable: true,
|
importable: true,
|
||||||
exportable: true,
|
|
||||||
defaultFilterField: 'name',
|
defaultFilterField: 'name',
|
||||||
defaultSort: {
|
defaultSort: {
|
||||||
sortField: 'name',
|
sortField: 'name',
|
||||||
@@ -122,97 +121,6 @@ export default {
|
|||||||
fieldType: 'date',
|
fieldType: 'date',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
type: {
|
|
||||||
name: 'item.field.type',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
name: 'item.field.name',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
code: {
|
|
||||||
name: 'item.field.code',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
sellable: {
|
|
||||||
name: 'item.field.sellable',
|
|
||||||
type: 'boolean',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
purchasable: {
|
|
||||||
name: 'item.field.purchasable',
|
|
||||||
type: 'boolean',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
sellPrice: {
|
|
||||||
name: 'item.field.cost_price',
|
|
||||||
type: 'number',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
costPrice: {
|
|
||||||
name: 'item.field.cost_account',
|
|
||||||
type: 'number',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
costAccount: {
|
|
||||||
name: 'item.field.sell_account',
|
|
||||||
type: 'text',
|
|
||||||
accessor: 'costAccount.name',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
sellAccount: {
|
|
||||||
name: 'item.field.sell_description',
|
|
||||||
type: 'text',
|
|
||||||
accessor: 'sellAccount.name',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
inventoryAccount: {
|
|
||||||
name: 'item.field.inventory_account',
|
|
||||||
type: 'text',
|
|
||||||
accessor: 'inventoryAccount.name',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
sellDescription: {
|
|
||||||
name: 'Sell description',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
purchaseDescription: {
|
|
||||||
name: 'Purchase description',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
quantityOnHand: {
|
|
||||||
name: 'item.field.quantity_on_hand',
|
|
||||||
type: 'number',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
note: {
|
|
||||||
name: 'item.field.note',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
category: {
|
|
||||||
name: 'item.field.category',
|
|
||||||
type: 'text',
|
|
||||||
accessor: 'category.name',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
active: {
|
|
||||||
name: 'item.field.active',
|
|
||||||
fieldType: 'boolean',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
name: 'item.field.created_at',
|
|
||||||
type: 'date',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields2: {
|
fields2: {
|
||||||
type: {
|
type: {
|
||||||
name: 'item.field.type',
|
name: 'item.field.type',
|
||||||
@@ -287,7 +195,7 @@ export default {
|
|||||||
fieldType: 'relation',
|
fieldType: 'relation',
|
||||||
relationModel: 'ItemCategory',
|
relationModel: 'ItemCategory',
|
||||||
relationImportMatch: ['name'],
|
relationImportMatch: ['name'],
|
||||||
importHint: 'Matches the category name.',
|
importHint: "Matches the category name."
|
||||||
},
|
},
|
||||||
active: {
|
active: {
|
||||||
name: 'item.field.active',
|
name: 'item.field.active',
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export default {
|
|||||||
sortOrder: 'DESC',
|
sortOrder: 'DESC',
|
||||||
},
|
},
|
||||||
importable: true,
|
importable: true,
|
||||||
exportable: true,
|
|
||||||
fields: {
|
fields: {
|
||||||
name: {
|
name: {
|
||||||
name: 'item_category.field.name',
|
name: 'item_category.field.name',
|
||||||
@@ -29,24 +28,6 @@ export default {
|
|||||||
columnType: 'date',
|
columnType: 'date',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
name: {
|
|
||||||
name: 'item_category.field.name',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
name: 'item_category.field.description',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
count: {
|
|
||||||
name: 'item_category.field.count',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
name: 'item_category.field.created_at',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields2: {
|
fields2: {
|
||||||
name: {
|
name: {
|
||||||
name: 'item_category.field.name',
|
name: 'item_category.field.name',
|
||||||
|
|||||||
@@ -5,9 +5,6 @@ export default {
|
|||||||
sortField: 'name',
|
sortField: 'name',
|
||||||
},
|
},
|
||||||
importable: true,
|
importable: true,
|
||||||
exportFlattenOn: 'entries',
|
|
||||||
|
|
||||||
exportable: true,
|
|
||||||
importAggregator: 'group',
|
importAggregator: 'group',
|
||||||
importAggregateOn: 'entries',
|
importAggregateOn: 'entries',
|
||||||
importAggregateBy: 'journalNumber',
|
importAggregateBy: 'journalNumber',
|
||||||
@@ -59,76 +56,6 @@ export default {
|
|||||||
fieldType: 'date',
|
fieldType: 'date',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
date: {
|
|
||||||
name: 'manual_journal.field.date',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
journalNumber: {
|
|
||||||
name: 'manual_journal.field.journal_number',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
reference: {
|
|
||||||
name: 'manual_journal.field.reference',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
journalType: {
|
|
||||||
name: 'manual_journal.field.journal_type',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
name: 'Amount',
|
|
||||||
accessor: 'formattedAmount',
|
|
||||||
},
|
|
||||||
currencyCode: {
|
|
||||||
name: 'manual_journal.field.currency',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
exchangeRate: {
|
|
||||||
name: 'manual_journal.field.exchange_rate',
|
|
||||||
type: 'number',
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
name: 'manual_journal.field.description',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
entries: {
|
|
||||||
name: 'Entries',
|
|
||||||
type: 'collection',
|
|
||||||
collectionOf: 'object',
|
|
||||||
columns: {
|
|
||||||
credit: {
|
|
||||||
name: 'Credit',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
debit: {
|
|
||||||
name: 'Debit',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
account: {
|
|
||||||
name: 'Account',
|
|
||||||
accessor: 'account.name',
|
|
||||||
},
|
|
||||||
contact: {
|
|
||||||
name: 'Contact',
|
|
||||||
accessor: 'contact.displayName',
|
|
||||||
},
|
|
||||||
note: {
|
|
||||||
name: 'Note',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
publish: {
|
|
||||||
name: 'Publish',
|
|
||||||
type: 'boolean',
|
|
||||||
},
|
|
||||||
publishedAt: {
|
|
||||||
name: 'Published At',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
name: 'Created At',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields2: {
|
fields2: {
|
||||||
date: {
|
date: {
|
||||||
name: 'manual_journal.field.date',
|
name: 'manual_journal.field.date',
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
export default {
|
export default {
|
||||||
importable: true,
|
importable: true,
|
||||||
exportable: true,
|
|
||||||
importAggregator: 'group',
|
importAggregator: 'group',
|
||||||
importAggregateOn: 'entries',
|
importAggregateOn: 'entries',
|
||||||
importAggregateBy: 'paymentReceiveNo',
|
importAggregateBy: 'paymentReceiveNo',
|
||||||
@@ -58,42 +57,6 @@ export default {
|
|||||||
fieldDate: 'date',
|
fieldDate: 'date',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
customer: {
|
|
||||||
name: 'payment_receive.field.customer',
|
|
||||||
accessor: 'customer.displayName',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
paymentDate: {
|
|
||||||
name: 'payment_receive.field.payment_date',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
name: 'payment_receive.field.amount',
|
|
||||||
type: 'number',
|
|
||||||
},
|
|
||||||
referenceNo: {
|
|
||||||
name: 'payment_receive.field.reference_no',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
depositAccount: {
|
|
||||||
name: 'payment_receive.field.deposit_account',
|
|
||||||
accessor: 'depositAccount.name',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
paymentReceiveNo: {
|
|
||||||
name: 'payment_receive.field.payment_receive_no',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
statement: {
|
|
||||||
name: 'payment_receive.field.statement',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
created_at: {
|
|
||||||
name: 'payment_receive.field.created_at',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields2: {
|
fields2: {
|
||||||
customerId: {
|
customerId: {
|
||||||
name: 'payment_receive.field.customer',
|
name: 'payment_receive.field.customer',
|
||||||
@@ -121,12 +84,12 @@ export default {
|
|||||||
relationModel: 'Account',
|
relationModel: 'Account',
|
||||||
relationImportMatch: ['name', 'code'],
|
relationImportMatch: ['name', 'code'],
|
||||||
required: true,
|
required: true,
|
||||||
importHint: 'Matches the account name or code.',
|
importHint: "Matches the account name or code."
|
||||||
},
|
},
|
||||||
paymentReceiveNo: {
|
paymentReceiveNo: {
|
||||||
name: 'payment_receive.field.payment_receive_no',
|
name: 'payment_receive.field.payment_receive_no',
|
||||||
fieldType: 'text',
|
fieldType: 'text',
|
||||||
importHint: 'The payment number should be unique.',
|
importHint: "The payment number should be unique."
|
||||||
},
|
},
|
||||||
statement: {
|
statement: {
|
||||||
name: 'payment_receive.field.statement',
|
name: 'payment_receive.field.statement',
|
||||||
@@ -145,7 +108,7 @@ export default {
|
|||||||
relationModel: 'SaleInvoice',
|
relationModel: 'SaleInvoice',
|
||||||
relationImportMatch: 'invoiceNo',
|
relationImportMatch: 'invoiceNo',
|
||||||
required: true,
|
required: true,
|
||||||
importHint: 'Matches the invoice number.',
|
importHint: "Matches the invoice number."
|
||||||
},
|
},
|
||||||
paymentAmount: {
|
paymentAmount: {
|
||||||
name: 'payment_receive.field.entries.payment_amount',
|
name: 'payment_receive.field.entries.payment_amount',
|
||||||
|
|||||||
@@ -4,9 +4,6 @@ export default {
|
|||||||
sortOrder: 'DESC',
|
sortOrder: 'DESC',
|
||||||
sortField: 'estimate_date',
|
sortField: 'estimate_date',
|
||||||
},
|
},
|
||||||
exportable: true,
|
|
||||||
exportFlattenOn: 'entries',
|
|
||||||
|
|
||||||
importable: true,
|
importable: true,
|
||||||
importAggregator: 'group',
|
importAggregator: 'group',
|
||||||
importAggregateOn: 'entries',
|
importAggregateOn: 'entries',
|
||||||
@@ -76,91 +73,6 @@ export default {
|
|||||||
columnType: 'date',
|
columnType: 'date',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
customer: {
|
|
||||||
name: 'Customer',
|
|
||||||
type: 'text',
|
|
||||||
accessor: 'customer.displayName',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
estimateDate: {
|
|
||||||
name: 'Estimate Date',
|
|
||||||
type: 'date',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
expirationDate: {
|
|
||||||
name: 'Expiration Date',
|
|
||||||
type: 'date',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
estimateNumber: {
|
|
||||||
name: 'Estimate No.',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
reference: {
|
|
||||||
name: 'Reference No.',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
name: 'Amount',
|
|
||||||
accessor: 'formattedAmount',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
exchangeRate: {
|
|
||||||
name: 'Exchange Rate',
|
|
||||||
type: 'number',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
currencyCode: {
|
|
||||||
name: 'Currency',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
note: {
|
|
||||||
name: 'Note',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
termsConditions: {
|
|
||||||
name: 'Terms & Conditions',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
delivered: {
|
|
||||||
name: 'Delivered',
|
|
||||||
type: 'boolean',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
entries: {
|
|
||||||
name: 'Entries',
|
|
||||||
accessor: 'entries',
|
|
||||||
type: 'collection',
|
|
||||||
collectionOf: 'object',
|
|
||||||
columns: {
|
|
||||||
itemName: {
|
|
||||||
name: 'Item Name',
|
|
||||||
accessor: 'item.name',
|
|
||||||
},
|
|
||||||
rate: {
|
|
||||||
name: 'Item Rate',
|
|
||||||
accessor: 'rateFormatted',
|
|
||||||
},
|
|
||||||
quantity: {
|
|
||||||
name: 'Item Quantity',
|
|
||||||
accessor: 'quantityFormatted',
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
name: 'Item Description',
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
name: 'Item Amount',
|
|
||||||
accessor: 'totalFormatted',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields2: {
|
fields2: {
|
||||||
customerId: {
|
customerId: {
|
||||||
name: 'Customer',
|
name: 'Customer',
|
||||||
@@ -220,7 +132,7 @@ export default {
|
|||||||
relationModel: 'Item',
|
relationModel: 'Item',
|
||||||
relationImportMatch: ['name', 'code'],
|
relationImportMatch: ['name', 'code'],
|
||||||
required: true,
|
required: true,
|
||||||
importHint: 'Matches the item name or code.',
|
importHint: "Matches the item name or code."
|
||||||
},
|
},
|
||||||
rate: {
|
rate: {
|
||||||
name: 'invoice.field.rate',
|
name: 'invoice.field.rate',
|
||||||
|
|||||||
@@ -4,9 +4,6 @@ export default {
|
|||||||
sortOrder: 'DESC',
|
sortOrder: 'DESC',
|
||||||
sortField: 'created_at',
|
sortField: 'created_at',
|
||||||
},
|
},
|
||||||
exportable: true,
|
|
||||||
exportFlattenOn: 'entries',
|
|
||||||
|
|
||||||
importable: true,
|
importable: true,
|
||||||
importAggregator: 'group',
|
importAggregator: 'group',
|
||||||
importAggregateOn: 'entries',
|
importAggregateOn: 'entries',
|
||||||
@@ -90,89 +87,6 @@ export default {
|
|||||||
fieldType: 'date',
|
fieldType: 'date',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
invoiceDate: {
|
|
||||||
name: 'invoice.field.invoice_date',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
dueDate: {
|
|
||||||
name: 'invoice.field.due_date',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
referenceNo: {
|
|
||||||
name: 'invoice.field.reference_no',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
invoiceNo: {
|
|
||||||
name: 'invoice.field.invoice_no',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
customer: {
|
|
||||||
name: 'invoice.field.customer',
|
|
||||||
type: 'text',
|
|
||||||
accessor: 'customer.displayName',
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
name: 'invoice.field.amount',
|
|
||||||
type: 'text',
|
|
||||||
accessor: 'balanceAmountFormatted',
|
|
||||||
},
|
|
||||||
exchangeRate: {
|
|
||||||
name: 'invoice.field.exchange_rate',
|
|
||||||
type: 'number',
|
|
||||||
},
|
|
||||||
currencyCode: {
|
|
||||||
name: 'invoice.field.currency',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
paidAmount: {
|
|
||||||
name: 'Paid Amount',
|
|
||||||
accessor: 'paymentAmountFormatted',
|
|
||||||
},
|
|
||||||
dueAmount: {
|
|
||||||
name: 'Due Amount',
|
|
||||||
accessor: 'dueAmountFormatted',
|
|
||||||
},
|
|
||||||
invoiceMessage: {
|
|
||||||
name: 'invoice.field.invoice_message',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
termsConditions: {
|
|
||||||
name: 'invoice.field.terms_conditions',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
delivered: {
|
|
||||||
name: 'invoice.field.delivered',
|
|
||||||
type: 'boolean',
|
|
||||||
},
|
|
||||||
entries: {
|
|
||||||
name: 'Entries',
|
|
||||||
accessor: 'entries',
|
|
||||||
type: 'collection',
|
|
||||||
collectionOf: 'object',
|
|
||||||
columns: {
|
|
||||||
itemName: {
|
|
||||||
name: 'Item Name',
|
|
||||||
accessor: 'item.name',
|
|
||||||
},
|
|
||||||
rate: {
|
|
||||||
name: 'Item Rate',
|
|
||||||
accessor: 'rateFormatted',
|
|
||||||
},
|
|
||||||
quantity: {
|
|
||||||
name: 'Item Quantity',
|
|
||||||
accessor: 'quantityFormatted',
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
name: 'Item Description',
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
name: 'Item Amount',
|
|
||||||
accessor: 'totalFormatted',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields2: {
|
fields2: {
|
||||||
invoiceDate: {
|
invoiceDate: {
|
||||||
name: 'invoice.field.invoice_date',
|
name: 'invoice.field.invoice_date',
|
||||||
@@ -228,7 +142,7 @@ export default {
|
|||||||
relationModel: 'Item',
|
relationModel: 'Item',
|
||||||
relationImportMatch: ['name', 'code'],
|
relationImportMatch: ['name', 'code'],
|
||||||
required: true,
|
required: true,
|
||||||
importHint: 'Matches the item name or code.',
|
importHint: "Matches the item name or code."
|
||||||
},
|
},
|
||||||
rate: {
|
rate: {
|
||||||
name: 'invoice.field.rate',
|
name: 'invoice.field.rate',
|
||||||
|
|||||||
@@ -4,9 +4,6 @@ export default {
|
|||||||
sortOrder: 'DESC',
|
sortOrder: 'DESC',
|
||||||
sortField: 'created_at',
|
sortField: 'created_at',
|
||||||
},
|
},
|
||||||
exportable: true,
|
|
||||||
exportFlattenOn: 'entries',
|
|
||||||
|
|
||||||
importable: true,
|
importable: true,
|
||||||
importAggregator: 'group',
|
importAggregator: 'group',
|
||||||
importAggregateOn: 'entries',
|
importAggregateOn: 'entries',
|
||||||
@@ -80,86 +77,6 @@ export default {
|
|||||||
sortCustomQuery: StatusFieldSortQuery,
|
sortCustomQuery: StatusFieldSortQuery,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
amount: {
|
|
||||||
name: 'receipt.field.amount',
|
|
||||||
column: 'amount',
|
|
||||||
type: 'number',
|
|
||||||
},
|
|
||||||
depositAccount: {
|
|
||||||
name: 'receipt.field.deposit_account',
|
|
||||||
type: 'text',
|
|
||||||
accessor: 'depositAccount.name',
|
|
||||||
},
|
|
||||||
customer: {
|
|
||||||
name: 'receipt.field.customer',
|
|
||||||
type: 'text',
|
|
||||||
accessor: 'customer.displayName',
|
|
||||||
},
|
|
||||||
receiptDate: {
|
|
||||||
name: 'receipt.field.receipt_date',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
receiptNumber: {
|
|
||||||
name: 'receipt.field.receipt_number',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
referenceNo: {
|
|
||||||
name: 'receipt.field.reference_no',
|
|
||||||
column: 'reference_no',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
receiptMessage: {
|
|
||||||
name: 'receipt.field.receipt_message',
|
|
||||||
column: 'receipt_message',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
statement: {
|
|
||||||
name: 'receipt.field.statement',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
name: 'receipt.field.status',
|
|
||||||
type: 'enumeration',
|
|
||||||
options: [
|
|
||||||
{ key: 'draft', label: 'receipt.field.status.draft' },
|
|
||||||
{ key: 'closed', label: 'receipt.field.status.closed' },
|
|
||||||
],
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
entries: {
|
|
||||||
name: 'Entries',
|
|
||||||
accessor: 'entries',
|
|
||||||
type: 'collection',
|
|
||||||
collectionOf: 'object',
|
|
||||||
columns: {
|
|
||||||
itemName: {
|
|
||||||
name: 'Item Name',
|
|
||||||
accessor: 'item.name',
|
|
||||||
},
|
|
||||||
rate: {
|
|
||||||
name: 'Item Rate',
|
|
||||||
accessor: 'rateFormatted',
|
|
||||||
},
|
|
||||||
quantity: {
|
|
||||||
name: 'Item Quantity',
|
|
||||||
accessor: 'quantityFormatted',
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
name: 'Item Description',
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
name: 'Item Amount',
|
|
||||||
accessor: 'totalFormatted',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
name: 'receipt.field.created_at',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields2: {
|
fields2: {
|
||||||
receiptDate: {
|
receiptDate: {
|
||||||
name: 'Receipt Date',
|
name: 'Receipt Date',
|
||||||
@@ -209,7 +126,7 @@ export default {
|
|||||||
relationModel: 'Item',
|
relationModel: 'Item',
|
||||||
relationImportMatch: ['name', 'code'],
|
relationImportMatch: ['name', 'code'],
|
||||||
required: true,
|
required: true,
|
||||||
importHint: 'Matches the item name or code.',
|
importHint: "Matches the item name or code."
|
||||||
},
|
},
|
||||||
rate: {
|
rate: {
|
||||||
name: 'invoice.field.rate',
|
name: 'invoice.field.rate',
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export default {
|
|||||||
sortField: 'created_at',
|
sortField: 'created_at',
|
||||||
},
|
},
|
||||||
importable: true,
|
importable: true,
|
||||||
exportable: true,
|
|
||||||
fields: {
|
fields: {
|
||||||
first_name: {
|
first_name: {
|
||||||
name: 'vendor.field.first_name',
|
name: 'vendor.field.first_name',
|
||||||
@@ -33,7 +32,7 @@ export default {
|
|||||||
fieldType: 'text',
|
fieldType: 'text',
|
||||||
},
|
},
|
||||||
personal_phone: {
|
personal_phone: {
|
||||||
name: 'vendor.field.personal_phone',
|
name: 'vendor.field.personal_pone',
|
||||||
column: 'personal_phone',
|
column: 'personal_phone',
|
||||||
fieldType: 'text',
|
fieldType: 'text',
|
||||||
},
|
},
|
||||||
@@ -91,154 +90,6 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
firstName: {
|
|
||||||
name: 'vendor.field.first_name',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
lastName: {
|
|
||||||
name: 'vendor.field.last_name',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
displayName: {
|
|
||||||
name: 'vendor.field.display_name',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
email: {
|
|
||||||
name: 'vendor.field.email',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
workPhone: {
|
|
||||||
name: 'vendor.field.work_phone',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
personalPhone: {
|
|
||||||
name: 'vendor.field.personal_phone',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
companyName: {
|
|
||||||
name: 'vendor.field.company_name',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
website: {
|
|
||||||
name: 'vendor.field.website',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
balance: {
|
|
||||||
name: 'vendor.field.balance',
|
|
||||||
type: 'number',
|
|
||||||
},
|
|
||||||
openingBalance: {
|
|
||||||
name: 'vendor.field.opening_balance',
|
|
||||||
type: 'number',
|
|
||||||
},
|
|
||||||
openingBalanceAt: {
|
|
||||||
name: 'vendor.field.opening_balance_at',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
currencyCode: {
|
|
||||||
name: 'vendor.field.currency',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
name: 'vendor.field.status',
|
|
||||||
},
|
|
||||||
note: {
|
|
||||||
name: 'vendor.field.note',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
// Billing Address
|
|
||||||
billingAddress1: {
|
|
||||||
name: 'Billing Address 1',
|
|
||||||
column: 'billing_address1',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
billingAddress2: {
|
|
||||||
name: 'Billing Address 2',
|
|
||||||
column: 'billing_address2',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
billingAddressCity: {
|
|
||||||
name: 'Billing Address City',
|
|
||||||
column: 'billing_address_city',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
billingAddressCountry: {
|
|
||||||
name: 'Billing Address Country',
|
|
||||||
column: 'billing_address_country',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
billingAddressPostcode: {
|
|
||||||
name: 'Billing Address Postcode',
|
|
||||||
column: 'billing_address_postcode',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
billingAddressState: {
|
|
||||||
name: 'Billing Address State',
|
|
||||||
column: 'billing_address_state',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
billingAddressPhone: {
|
|
||||||
name: 'Billing Address Phone',
|
|
||||||
column: 'billing_address_phone',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
// Shipping Address
|
|
||||||
shippingAddress1: {
|
|
||||||
name: 'Shipping Address 1',
|
|
||||||
column: 'shipping_address1',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
shippingAddress2: {
|
|
||||||
name: 'Shipping Address 2',
|
|
||||||
column: 'shipping_address2',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
shippingAddressCity: {
|
|
||||||
name: 'Shipping Address City',
|
|
||||||
column: 'shipping_address_city',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
shippingAddressCountry: {
|
|
||||||
name: 'Shipping Address Country',
|
|
||||||
column: 'shipping_address_country',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
shippingAddressPostcode: {
|
|
||||||
name: 'Shipping Address Postcode',
|
|
||||||
column: 'shipping_address_postcode',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
shippingAddressState: {
|
|
||||||
name: 'Shipping Address State',
|
|
||||||
column: 'shipping_address_state',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
shippingAddressPhone: {
|
|
||||||
name: 'Shipping Address Phone',
|
|
||||||
column: 'shipping_address_phone',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
name: 'vendor.field.created_at',
|
|
||||||
type: 'date',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields2: {
|
fields2: {
|
||||||
firstName: {
|
firstName: {
|
||||||
name: 'vendor.field.first_name',
|
name: 'vendor.field.first_name',
|
||||||
|
|||||||
@@ -12,14 +12,10 @@ export default {
|
|||||||
sortOrder: 'DESC',
|
sortOrder: 'DESC',
|
||||||
sortField: 'name',
|
sortField: 'name',
|
||||||
},
|
},
|
||||||
exportable: true,
|
|
||||||
exportFlattenOn: 'entries',
|
|
||||||
|
|
||||||
importable: true,
|
importable: true,
|
||||||
importAggregator: 'group',
|
importAggregator: 'group',
|
||||||
importAggregateOn: 'entries',
|
importAggregateOn: 'entries',
|
||||||
importAggregateBy: 'vendorCreditNumber',
|
importAggregateBy: 'vendorCreditNumber',
|
||||||
|
|
||||||
fields: {
|
fields: {
|
||||||
vendor: {
|
vendor: {
|
||||||
name: 'vendor_credit.field.vendor',
|
name: 'vendor_credit.field.vendor',
|
||||||
@@ -80,79 +76,6 @@ export default {
|
|||||||
fieldType: 'date',
|
fieldType: 'date',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
vendorId: {
|
|
||||||
name: 'Vendor',
|
|
||||||
type: 'relation',
|
|
||||||
accessor: 'vendor.displayName',
|
|
||||||
},
|
|
||||||
exchangeRate: {
|
|
||||||
name: 'Echange Rate',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
vendorCreditNumber: {
|
|
||||||
name: 'Vendor Credit No.',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
referenceNo: {
|
|
||||||
name: 'Refernece No.',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
vendorCreditDate: {
|
|
||||||
name: 'Vendor Credit Date',
|
|
||||||
type: 'date',
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
name: 'Amount',
|
|
||||||
accessor: 'formattedAmount',
|
|
||||||
},
|
|
||||||
creditRemaining: {
|
|
||||||
name: 'Credits Remaining',
|
|
||||||
accessor: 'formattedCreditsRemaining',
|
|
||||||
},
|
|
||||||
refundedAmount: {
|
|
||||||
name: 'Refunded Amount',
|
|
||||||
accessor: 'refundedAmount',
|
|
||||||
},
|
|
||||||
invoicedAmount: {
|
|
||||||
name: 'Invoiced Amount',
|
|
||||||
accessor: 'formattedInvoicedAmount',
|
|
||||||
},
|
|
||||||
note: {
|
|
||||||
name: 'Note',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
open: {
|
|
||||||
name: 'Open',
|
|
||||||
type: 'boolean',
|
|
||||||
},
|
|
||||||
entries: {
|
|
||||||
name: 'Entries',
|
|
||||||
type: 'collection',
|
|
||||||
collectionOf: 'object',
|
|
||||||
columns: {
|
|
||||||
itemName: {
|
|
||||||
name: 'Item Name',
|
|
||||||
accessor: 'item.name',
|
|
||||||
},
|
|
||||||
rate: {
|
|
||||||
name: 'Item Rate',
|
|
||||||
accessor: 'rateFormatted',
|
|
||||||
},
|
|
||||||
quantity: {
|
|
||||||
name: 'Item Quantity',
|
|
||||||
accessor: 'quantityFormatted',
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
name: 'Item Description',
|
|
||||||
},
|
|
||||||
amount: {
|
|
||||||
name: 'Item Amount',
|
|
||||||
accessor: 'totalFormatted',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields2: {
|
fields2: {
|
||||||
vendorId: {
|
vendorId: {
|
||||||
name: 'Vendor',
|
name: 'Vendor',
|
||||||
@@ -199,7 +122,7 @@ export default {
|
|||||||
relationModel: 'Item',
|
relationModel: 'Item',
|
||||||
relationImportMatch: ['name', 'code'],
|
relationImportMatch: ['name', 'code'],
|
||||||
required: true,
|
required: true,
|
||||||
importHint: 'Matches the item name or code.',
|
importHint: "Matches the item name or code."
|
||||||
},
|
},
|
||||||
rate: {
|
rate: {
|
||||||
name: 'Rate',
|
name: 'Rate',
|
||||||
|
|||||||
@@ -8,34 +8,6 @@ export default {
|
|||||||
sortField: 'name',
|
sortField: 'name',
|
||||||
sortOrder: 'DESC',
|
sortOrder: 'DESC',
|
||||||
},
|
},
|
||||||
columns: {
|
|
||||||
date: {
|
|
||||||
name: 'warehouse_transfer.field.date',
|
|
||||||
type: 'date',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
transaction_number: {
|
|
||||||
name: 'warehouse_transfer.field.transaction_number',
|
|
||||||
type: 'text',
|
|
||||||
exportable: true,
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
name: 'warehouse_transfer.field.status',
|
|
||||||
fieldType: 'enumeration',
|
|
||||||
options: [
|
|
||||||
{ key: 'draft', label: 'Draft' },
|
|
||||||
{ key: 'in-transit', label: 'In Transit' },
|
|
||||||
{ key: 'transferred', label: 'Transferred' },
|
|
||||||
],
|
|
||||||
sortable: false,
|
|
||||||
},
|
|
||||||
created_at: {
|
|
||||||
name: 'warehouse_transfer.field.created_at',
|
|
||||||
column: 'created_at',
|
|
||||||
columnType: 'date',
|
|
||||||
fieldType: 'date',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields: {
|
fields: {
|
||||||
date: {
|
date: {
|
||||||
name: 'warehouse_transfer.field.date',
|
name: 'warehouse_transfer.field.date',
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'reflect-metadata'; // We need this in order to use @Decorators
|
import 'reflect-metadata'; // We need this in order to use @Decorators
|
||||||
import 'newrelic';
|
|
||||||
import './before';
|
import './before';
|
||||||
import '@/config';
|
import '@/config';
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { AccountsApplication } from './AccountsApplication';
|
|
||||||
import { Exportable } from '../Export/Exportable';
|
|
||||||
import { IAccountsFilter, IAccountsStructureType } from '@/interfaces';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class AccountsExportable extends Exportable {
|
|
||||||
@Inject()
|
|
||||||
private accountsApplication: AccountsApplication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the accounts data to exportable sheet.
|
|
||||||
* @param {number} tenantId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public exportable(tenantId: number, query: IAccountsFilter) {
|
|
||||||
const parsedQuery = {
|
|
||||||
sortOrder: 'desc',
|
|
||||||
columnSortBy: 'created_at',
|
|
||||||
inactiveMode: false,
|
|
||||||
...query,
|
|
||||||
structure: IAccountsStructureType.Flat,
|
|
||||||
pageSize: 12000,
|
|
||||||
page: 1,
|
|
||||||
} as IAccountsFilter;
|
|
||||||
|
|
||||||
return this.accountsApplication
|
|
||||||
.getAccounts(tenantId, parsedQuery)
|
|
||||||
.then((output) => output.accounts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { Inject, Service } from "typedi";
|
|
||||||
import { UploadDocument } from "./UploadDocument";
|
|
||||||
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class AttachmentsApplication {
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
private uploadDocumentService: UploadDocument;
|
|
||||||
|
|
||||||
upload(data: any) {
|
|
||||||
return this.uploadDocumentService.upload(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(attachmentId: string) {}
|
|
||||||
|
|
||||||
get(attachmentId: string) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { Service } from 'typedi';
|
|
||||||
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class UploadDocument {
|
|
||||||
// setting up s3 client
|
|
||||||
public s3 = new S3Client({
|
|
||||||
region: process.env.AWS_REGION,
|
|
||||||
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
||||||
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
||||||
});
|
|
||||||
|
|
||||||
async upload(data) {
|
|
||||||
const putObjectCommand = new PutObjectCommand({
|
|
||||||
Bucket: process.env.AWS_BUCKET,
|
|
||||||
Key: data.filename,
|
|
||||||
Body: await data.toBuffer(),
|
|
||||||
ContentType: data.mimetype,
|
|
||||||
});
|
|
||||||
await this.s3.send(putObjectCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Service, Inject } from 'typedi';
|
import { Service, Inject, Container } from 'typedi';
|
||||||
import {
|
import {
|
||||||
IRegisterDTO,
|
IRegisterDTO,
|
||||||
ISystemUser,
|
ISystemUser,
|
||||||
@@ -9,9 +9,6 @@ import { AuthSigninService } from './AuthSignin';
|
|||||||
import { AuthSignupService } from './AuthSignup';
|
import { AuthSignupService } from './AuthSignup';
|
||||||
import { AuthSendResetPassword } from './AuthSendResetPassword';
|
import { AuthSendResetPassword } from './AuthSendResetPassword';
|
||||||
import { GetAuthMeta } from './GetAuthMeta';
|
import { GetAuthMeta } from './GetAuthMeta';
|
||||||
import { AuthSignupConfirmService } from './AuthSignupConfirm';
|
|
||||||
import { SystemUser } from '@/system/models';
|
|
||||||
import { AuthSignupConfirmResend } from './AuthSignupResend';
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export default class AuthenticationApplication {
|
export default class AuthenticationApplication {
|
||||||
@@ -21,12 +18,6 @@ export default class AuthenticationApplication {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private authSignupService: AuthSignupService;
|
private authSignupService: AuthSignupService;
|
||||||
|
|
||||||
@Inject()
|
|
||||||
private authSignupConfirmService: AuthSignupConfirmService;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
private authSignUpConfirmResendService: AuthSignupConfirmResend;
|
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private authResetPasswordService: AuthSendResetPassword;
|
private authResetPasswordService: AuthSendResetPassword;
|
||||||
|
|
||||||
@@ -53,28 +44,6 @@ export default class AuthenticationApplication {
|
|||||||
return this.authSignupService.signUp(signupDTO);
|
return this.authSignupService.signUp(signupDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Verfying the provided user's email after signin-up.
|
|
||||||
* @param {string} email
|
|
||||||
* @param {string} token
|
|
||||||
* @returns {Promise<SystemUser>}
|
|
||||||
*/
|
|
||||||
public async signUpConfirm(
|
|
||||||
email: string,
|
|
||||||
token: string
|
|
||||||
): Promise<SystemUser> {
|
|
||||||
return this.authSignupConfirmService.signUpConfirm(email, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resends the confirmation email of the given system user.
|
|
||||||
* @param {number} userId - System user id.
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
public async signUpConfirmResend(userId: number) {
|
|
||||||
return this.authSignUpConfirmResendService.signUpConfirmResend(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates and retrieve password reset token for the given user email.
|
* Generates and retrieve password reset token for the given user email.
|
||||||
* @param {string} email
|
* @param {string} email
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { defaultTo, isEmpty, omit } from 'lodash';
|
import { isEmpty, omit } from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import crypto from 'crypto';
|
|
||||||
import { ServiceError } from '@/exceptions';
|
import { ServiceError } from '@/exceptions';
|
||||||
import {
|
import {
|
||||||
IAuthSignedUpEventPayload,
|
IAuthSignedUpEventPayload,
|
||||||
@@ -43,13 +42,6 @@ export class AuthSignupService {
|
|||||||
|
|
||||||
const hashedPassword = await hashPassword(signupDTO.password);
|
const hashedPassword = await hashPassword(signupDTO.password);
|
||||||
|
|
||||||
const verifyTokenCrypto = crypto.randomBytes(64).toString('hex');
|
|
||||||
const verifiedEnabed = defaultTo(config.signupConfirmation.enabled, false);
|
|
||||||
const verifyToken = verifiedEnabed ? verifyTokenCrypto : '';
|
|
||||||
const verified = !verifiedEnabed;
|
|
||||||
|
|
||||||
const inviteAcceptedAt = moment().format('YYYY-MM-DD');
|
|
||||||
|
|
||||||
// Triggers signin up event.
|
// Triggers signin up event.
|
||||||
await this.eventPublisher.emitAsync(events.auth.signingUp, {
|
await this.eventPublisher.emitAsync(events.auth.signingUp, {
|
||||||
signupDTO,
|
signupDTO,
|
||||||
@@ -58,12 +50,10 @@ export class AuthSignupService {
|
|||||||
const tenant = await this.tenantsManager.createTenant();
|
const tenant = await this.tenantsManager.createTenant();
|
||||||
const registeredUser = await systemUserRepository.create({
|
const registeredUser = await systemUserRepository.create({
|
||||||
...omit(signupDTO, 'country'),
|
...omit(signupDTO, 'country'),
|
||||||
verifyToken,
|
|
||||||
verified,
|
|
||||||
active: true,
|
active: true,
|
||||||
password: hashedPassword,
|
password: hashedPassword,
|
||||||
tenantId: tenant.id,
|
tenantId: tenant.id,
|
||||||
inviteAcceptedAt,
|
inviteAcceptedAt: moment().format('YYYY-MM-DD'),
|
||||||
});
|
});
|
||||||
// Triggers signed up event.
|
// Triggers signed up event.
|
||||||
await this.eventPublisher.emitAsync(events.auth.signUp, {
|
await this.eventPublisher.emitAsync(events.auth.signUp, {
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { ServiceError } from '@/exceptions';
|
|
||||||
import { SystemUser } from '@/system/models';
|
|
||||||
import { ERRORS } from './_constants';
|
|
||||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
|
||||||
import events from '@/subscribers/events';
|
|
||||||
import {
|
|
||||||
IAuthSignUpVerifiedEventPayload,
|
|
||||||
IAuthSignUpVerifingEventPayload,
|
|
||||||
} from '@/interfaces';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class AuthSignupConfirmService {
|
|
||||||
@Inject()
|
|
||||||
private eventPublisher: EventPublisher;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies the provided user's email after signing-up.
|
|
||||||
* @throws {ServiceErrors}
|
|
||||||
* @param {IRegisterDTO} signupDTO
|
|
||||||
* @returns {Promise<ISystemUser>}
|
|
||||||
*/
|
|
||||||
public async signUpConfirm(
|
|
||||||
email: string,
|
|
||||||
verifyToken: string
|
|
||||||
): Promise<SystemUser> {
|
|
||||||
const foundUser = await SystemUser.query().findOne({ email, verifyToken });
|
|
||||||
|
|
||||||
if (!foundUser) {
|
|
||||||
throw new ServiceError(ERRORS.SIGNUP_CONFIRM_TOKEN_INVALID);
|
|
||||||
}
|
|
||||||
const userId = foundUser.id;
|
|
||||||
|
|
||||||
// Triggers `signUpConfirming` event.
|
|
||||||
await this.eventPublisher.emitAsync(events.auth.signUpConfirming, {
|
|
||||||
email,
|
|
||||||
verifyToken,
|
|
||||||
userId,
|
|
||||||
} as IAuthSignUpVerifingEventPayload);
|
|
||||||
|
|
||||||
const updatedUser = await SystemUser.query().patchAndFetchById(
|
|
||||||
foundUser.id,
|
|
||||||
{
|
|
||||||
verified: true,
|
|
||||||
verifyToken: '',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
// Triggers `signUpConfirmed` event.
|
|
||||||
await this.eventPublisher.emitAsync(events.auth.signUpConfirmed, {
|
|
||||||
email,
|
|
||||||
verifyToken,
|
|
||||||
userId,
|
|
||||||
} as IAuthSignUpVerifiedEventPayload);
|
|
||||||
|
|
||||||
return updatedUser as SystemUser;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { ServiceError } from '@/exceptions';
|
|
||||||
import { SystemUser } from '@/system/models';
|
|
||||||
import { ERRORS } from './_constants';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class AuthSignupConfirmResend {
|
|
||||||
@Inject('agenda')
|
|
||||||
private agenda: any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resends the email confirmation of the given user.
|
|
||||||
* @param {number} userId - User ID.
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
public async signUpConfirmResend(userId: number) {
|
|
||||||
const user = await SystemUser.query().findById(userId).throwIfNotFound();
|
|
||||||
|
|
||||||
// Throw error if the user is already verified.
|
|
||||||
if (user.verified) {
|
|
||||||
throw new ServiceError(ERRORS.USER_ALREADY_VERIFIED);
|
|
||||||
}
|
|
||||||
// Throw error if the verification token is not exist.
|
|
||||||
if (!user.verifyToken) {
|
|
||||||
throw new ServiceError(ERRORS.USER_ALREADY_VERIFIED);
|
|
||||||
}
|
|
||||||
const payload = {
|
|
||||||
email: user.email,
|
|
||||||
token: user.verifyToken,
|
|
||||||
fullName: user.firstName,
|
|
||||||
};
|
|
||||||
await this.agenda.now('send-signup-verify-mail', payload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -33,33 +33,4 @@ export default class AuthenticationMailMesssages {
|
|||||||
})
|
})
|
||||||
.send();
|
.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends signup verification mail.
|
|
||||||
* @param {string} email - Email address
|
|
||||||
* @param {string} fullName - User name.
|
|
||||||
* @param {string} token - Verification token.
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
public async sendSignupVerificationMail(
|
|
||||||
email: string,
|
|
||||||
fullName: string,
|
|
||||||
token: string
|
|
||||||
) {
|
|
||||||
const verifyUrl = `${config.baseURL}/auth/email_confirmation?token=${token}&email=${email}`;
|
|
||||||
|
|
||||||
await new Mail()
|
|
||||||
.setSubject('Bigcapital - Verify your email')
|
|
||||||
.setView('mail/SignupVerifyEmail.html')
|
|
||||||
.setTo(email)
|
|
||||||
.setAttachments([
|
|
||||||
{
|
|
||||||
filename: 'bigcapital.png',
|
|
||||||
path: `${global.__views_dir}/images/bigcapital.png`,
|
|
||||||
cid: 'bigcapital_logo',
|
|
||||||
},
|
|
||||||
])
|
|
||||||
.setData({ verifyUrl, fullName })
|
|
||||||
.send();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,4 @@ export const ERRORS = {
|
|||||||
EMAIL_EXISTS: 'EMAIL_EXISTS',
|
EMAIL_EXISTS: 'EMAIL_EXISTS',
|
||||||
SIGNUP_RESTRICTED_NOT_ALLOWED: 'SIGNUP_RESTRICTED_NOT_ALLOWED',
|
SIGNUP_RESTRICTED_NOT_ALLOWED: 'SIGNUP_RESTRICTED_NOT_ALLOWED',
|
||||||
SIGNUP_RESTRICTED: 'SIGNUP_RESTRICTED',
|
SIGNUP_RESTRICTED: 'SIGNUP_RESTRICTED',
|
||||||
SIGNUP_CONFIRM_TOKEN_INVALID: 'SIGNUP_CONFIRM_TOKEN_INVALID',
|
|
||||||
USER_ALREADY_VERIFIED: 'USER_ALREADY_VERIFIED',
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
import { IAuthSignedUpEventPayload } from '@/interfaces';
|
|
||||||
import events from '@/subscribers/events';
|
|
||||||
import { Inject } from 'typedi';
|
|
||||||
|
|
||||||
export class SendVerfiyMailOnSignUp {
|
|
||||||
@Inject('agenda')
|
|
||||||
private agenda: any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attaches events with handles.
|
|
||||||
*/
|
|
||||||
public attach(bus) {
|
|
||||||
bus.subscribe(events.auth.signUp, this.handleSendVerifyMailOnSignup);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {ITaxRateEditedPayload} payload -
|
|
||||||
*/
|
|
||||||
private handleSendVerifyMailOnSignup = async ({
|
|
||||||
user,
|
|
||||||
}: IAuthSignedUpEventPayload) => {
|
|
||||||
const payload = {
|
|
||||||
email: user.email,
|
|
||||||
token: user.verifyToken,
|
|
||||||
fullName: user.firstName,
|
|
||||||
};
|
|
||||||
await this.agenda.now('send-signup-verify-mail', payload);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import { Container } from 'typedi';
|
|
||||||
import AuthenticationMailMesssages from '@/services/Authentication/AuthenticationMailMessages';
|
|
||||||
|
|
||||||
export class SendVerifyMailJob {
|
|
||||||
/**
|
|
||||||
* Constructor method.
|
|
||||||
* @param {Agenda} agenda
|
|
||||||
*/
|
|
||||||
constructor(agenda) {
|
|
||||||
agenda.define(
|
|
||||||
'send-signup-verify-mail',
|
|
||||||
{ 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 { data } = job.attrs;
|
|
||||||
const { email, fullName, token } = data;
|
|
||||||
const authService = Container.get(AuthenticationMailMesssages);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await authService.sendSignupVerificationMail(email, fullName, token);
|
|
||||||
done();
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
done(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { IItemsFilter } from '@/interfaces';
|
|
||||||
import { CustomersApplication } from './CustomersApplication';
|
|
||||||
import { Exportable } from '@/services/Export/Exportable';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class CustomersExportable extends Exportable {
|
|
||||||
@Inject()
|
|
||||||
private customersApplication: CustomersApplication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the accounts data to exportable sheet.
|
|
||||||
* @param {number} tenantId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public exportable(tenantId: number, query: IItemsFilter) {
|
|
||||||
const parsedQuery = {
|
|
||||||
sortOrder: 'DESC',
|
|
||||||
columnSortBy: 'created_at',
|
|
||||||
page: 1,
|
|
||||||
...query,
|
|
||||||
pageSize: 12,
|
|
||||||
} as IItemsFilter;
|
|
||||||
|
|
||||||
return this.customersApplication
|
|
||||||
.getCustomers(tenantId, parsedQuery)
|
|
||||||
.then((output) => output.customers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { IItemsFilter } from '@/interfaces';
|
|
||||||
import { Exportable } from '@/services/Export/Exportable';
|
|
||||||
import { VendorsApplication } from './VendorsApplication';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class VendorsExportable extends Exportable {
|
|
||||||
@Inject()
|
|
||||||
private vendorsApplication: VendorsApplication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the accounts data to exportable sheet.
|
|
||||||
* @param {number} tenantId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public exportable(tenantId: number, query: IItemsFilter) {
|
|
||||||
const parsedQuery = {
|
|
||||||
sortOrder: 'DESC',
|
|
||||||
columnSortBy: 'created_at',
|
|
||||||
page: 1,
|
|
||||||
...query,
|
|
||||||
pageSize: 12,
|
|
||||||
} as IItemsFilter;
|
|
||||||
|
|
||||||
return this.vendorsApplication
|
|
||||||
.getVendors(tenantId, parsedQuery)
|
|
||||||
.then((output) => output.vendors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { ICreditNotesQueryDTO } from '@/interfaces';
|
|
||||||
import { Exportable } from '@/services/Export/Exportable';
|
|
||||||
import ListCreditNotes from './ListCreditNotes';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class CreditNotesExportable extends Exportable {
|
|
||||||
@Inject()
|
|
||||||
private getCreditNotes: ListCreditNotes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the accounts data to exportable sheet.
|
|
||||||
* @param {number} tenantId -
|
|
||||||
* @param {IVendorCreditsQueryDTO} query -
|
|
||||||
* @returns {}
|
|
||||||
*/
|
|
||||||
public exportable(tenantId: number, query: ICreditNotesQueryDTO) {
|
|
||||||
const parsedQuery = {
|
|
||||||
sortOrder: 'desc',
|
|
||||||
columnSortBy: 'created_at',
|
|
||||||
...query,
|
|
||||||
page: 1,
|
|
||||||
pageSize: 12000,
|
|
||||||
} as ICreditNotesQueryDTO;
|
|
||||||
|
|
||||||
return this.getCreditNotes
|
|
||||||
.getCreditNotesList(tenantId, parsedQuery)
|
|
||||||
.then((output) => output.creditNotes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -45,7 +45,7 @@ export default class ListCreditNotes extends BaseCreditNotes {
|
|||||||
);
|
);
|
||||||
const { results, pagination } = await CreditNote.query()
|
const { results, pagination } = await CreditNote.query()
|
||||||
.onBuild((builder) => {
|
.onBuild((builder) => {
|
||||||
builder.withGraphFetched('entries.item');
|
builder.withGraphFetched('entries');
|
||||||
builder.withGraphFetched('customer');
|
builder.withGraphFetched('customer');
|
||||||
dynamicFilter.buildQuery()(builder);
|
dynamicFilter.buildQuery()(builder);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { Exportable } from '../Export/Exportable';
|
|
||||||
import { IExpensesFilter } from '@/interfaces';
|
|
||||||
import { ExpensesApplication } from './ExpensesApplication';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class ExpensesExportable extends Exportable {
|
|
||||||
@Inject()
|
|
||||||
private expensesApplication: ExpensesApplication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the accounts data to exportable sheet.
|
|
||||||
* @param {number} tenantId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public exportable(tenantId: number, query: IExpensesFilter) {
|
|
||||||
const parsedQuery = {
|
|
||||||
sortOrder: 'desc',
|
|
||||||
columnSortBy: 'created_at',
|
|
||||||
...query,
|
|
||||||
page: 1,
|
|
||||||
pageSize: 12000,
|
|
||||||
} as IExpensesFilter;
|
|
||||||
|
|
||||||
return this.expensesApplication
|
|
||||||
.getExpenses(tenantId, parsedQuery)
|
|
||||||
.then((output) => output.expenses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { ExportResourceService } from './ExportService';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class ExportApplication {
|
|
||||||
@Inject()
|
|
||||||
private exportResource: ExportResourceService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exports the given resource to csv, xlsx or pdf format.
|
|
||||||
* @param {string} reosurce
|
|
||||||
* @param {string} format
|
|
||||||
*/
|
|
||||||
public export(tenantId: number, resource: string, format: string) {
|
|
||||||
return this.exportResource.export(tenantId, resource, format);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import { camelCase, upperFirst } from 'lodash';
|
|
||||||
import { Exportable } from './Exportable';
|
|
||||||
|
|
||||||
export class ExportableRegistry {
|
|
||||||
private static instance: ExportableRegistry;
|
|
||||||
private exportables: Record<string, Exportable>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor method.
|
|
||||||
*/
|
|
||||||
constructor() {
|
|
||||||
this.exportables = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets singleton instance of registry.
|
|
||||||
* @returns {ExportableRegistry}
|
|
||||||
*/
|
|
||||||
public static getInstance(): ExportableRegistry {
|
|
||||||
if (!ExportableRegistry.instance) {
|
|
||||||
ExportableRegistry.instance = new ExportableRegistry();
|
|
||||||
}
|
|
||||||
return ExportableRegistry.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers the given importable service.
|
|
||||||
* @param {string} resource
|
|
||||||
* @param {Exportable} importable
|
|
||||||
*/
|
|
||||||
public registerExportable(resource: string, importable: Exportable): void {
|
|
||||||
const _resource = this.sanitizeResourceName(resource);
|
|
||||||
this.exportables[_resource] = importable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the importable service instance of the given resource name.
|
|
||||||
* @param {string} name
|
|
||||||
* @returns {Exportable}
|
|
||||||
*/
|
|
||||||
public getExportable(name: string): Exportable {
|
|
||||||
const _name = this.sanitizeResourceName(name);
|
|
||||||
return this.exportables[_name];
|
|
||||||
}
|
|
||||||
|
|
||||||
private sanitizeResourceName(resource: string) {
|
|
||||||
return upperFirst(camelCase(resource));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import Container, { Service } from 'typedi';
|
|
||||||
import { AccountsExportable } from '../Accounts/AccountsExportable';
|
|
||||||
import { ExportableRegistry } from './ExportRegistery';
|
|
||||||
import { ItemsExportable } from '../Items/ItemsExportable';
|
|
||||||
import { CustomersExportable } from '../Contacts/Customers/CustomersExportable';
|
|
||||||
import { VendorsExportable } from '../Contacts/Vendors/VendorsExportable';
|
|
||||||
import { ExpensesExportable } from '../Expenses/ExpensesExportable';
|
|
||||||
import { SaleInvoicesExportable } from '../Sales/Invoices/SaleInvoicesExportable';
|
|
||||||
import { SaleEstimatesExportable } from '../Sales/Estimates/SaleEstimatesExportable';
|
|
||||||
import { SaleReceiptsExportable } from '../Sales/Receipts/SaleReceiptsExportable';
|
|
||||||
import { BillsExportable } from '../Purchases/Bills/BillsExportable';
|
|
||||||
import { PaymentsReceivedExportable } from '../Sales/PaymentReceives/PaymentsReceivedExportable';
|
|
||||||
import { BillPaymentExportable } from '../Purchases/BillPayments/BillPaymentExportable';
|
|
||||||
import { ManualJournalsExportable } from '../ManualJournals/ManualJournalExportable';
|
|
||||||
import { CreditNotesExportable } from '../CreditNotes/CreditNotesExportable';
|
|
||||||
import { VendorCreditsExportable } from '../Purchases/VendorCredits/VendorCreditsExportable';
|
|
||||||
import { ItemCategoriesExportable } from '../ItemCategories/ItemCategoriesExportable';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class ExportableResources {
|
|
||||||
private static registry: ExportableRegistry;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Consttuctor method.
|
|
||||||
*/
|
|
||||||
constructor() {
|
|
||||||
this.boot();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Importable instances.
|
|
||||||
*/
|
|
||||||
private importables = [
|
|
||||||
{ resource: 'Account', exportable: AccountsExportable },
|
|
||||||
{ resource: 'Item', exportable: ItemsExportable },
|
|
||||||
{ resource: 'ItemCategory', exportable: ItemCategoriesExportable },
|
|
||||||
{ resource: 'Customer', exportable: CustomersExportable },
|
|
||||||
{ resource: 'Vendor', exportable: VendorsExportable },
|
|
||||||
{ resource: 'Expense', exportable: ExpensesExportable },
|
|
||||||
{ resource: 'SaleInvoice', exportable: SaleInvoicesExportable },
|
|
||||||
{ resource: 'SaleEstimate', exportable: SaleEstimatesExportable },
|
|
||||||
{ resource: 'SaleReceipt', exportable: SaleReceiptsExportable },
|
|
||||||
{ resource: 'Bill', exportable: BillsExportable },
|
|
||||||
{ resource: 'PaymentReceive', exportable: PaymentsReceivedExportable },
|
|
||||||
{ resource: 'BillPayment', exportable: BillPaymentExportable },
|
|
||||||
{ resource: 'ManualJournal', exportable: ManualJournalsExportable },
|
|
||||||
{ resource: 'CreditNote', exportable: CreditNotesExportable },
|
|
||||||
{ resource: 'VendorCredit', exportable: VendorCreditsExportable },
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public get registry() {
|
|
||||||
return ExportableResources.registry;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Boots all the registered importables.
|
|
||||||
*/
|
|
||||||
public boot() {
|
|
||||||
if (!ExportableResources.registry) {
|
|
||||||
const instance = ExportableRegistry.getInstance();
|
|
||||||
|
|
||||||
this.importables.forEach((importable) => {
|
|
||||||
const importableInstance = Container.get(importable.exportable);
|
|
||||||
instance.registerExportable(importable.resource, importableInstance);
|
|
||||||
});
|
|
||||||
ExportableResources.registry = instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import xlsx from 'xlsx';
|
|
||||||
import * as R from 'ramda';
|
|
||||||
import { get } from 'lodash';
|
|
||||||
import { sanitizeResourceName } from '../Import/_utils';
|
|
||||||
import ResourceService from '../Resource/ResourceService';
|
|
||||||
import { ExportableResources } from './ExportResources';
|
|
||||||
import { ServiceError } from '@/exceptions';
|
|
||||||
import { Errors } from './common';
|
|
||||||
import { IModelMeta, IModelMetaColumn } from '@/interfaces';
|
|
||||||
import { flatDataCollections, getDataAccessor } from './utils';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class ExportResourceService {
|
|
||||||
@Inject()
|
|
||||||
private resourceService: ResourceService;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
private exportableResources: ExportableResources;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exports the given resource data through csv, xlsx or pdf.
|
|
||||||
* @param {number} tenantId - Tenant id.
|
|
||||||
* @param {string} resourceName - Resource name.
|
|
||||||
* @param {string} format - File format.
|
|
||||||
*/
|
|
||||||
public async export(tenantId: number, resourceName: string, format: string = 'csv') {
|
|
||||||
const resource = sanitizeResourceName(resourceName);
|
|
||||||
const resourceMeta = this.getResourceMeta(tenantId, resource);
|
|
||||||
|
|
||||||
this.validateResourceMeta(resourceMeta);
|
|
||||||
|
|
||||||
const data = await this.getExportableData(tenantId, resource);
|
|
||||||
const transformed = this.transformExportedData(tenantId, resource, data);
|
|
||||||
const exportableColumns = this.getExportableColumns(resourceMeta);
|
|
||||||
const workbook = this.createWorkbook(transformed, exportableColumns);
|
|
||||||
|
|
||||||
return this.exportWorkbook(workbook, format);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves metadata for a specific resource.
|
|
||||||
* @param {number} tenantId - The tenant identifier.
|
|
||||||
* @param {string} resource - The name of the resource.
|
|
||||||
* @returns The metadata of the resource.
|
|
||||||
*/
|
|
||||||
private getResourceMeta(tenantId: number, resource: string) {
|
|
||||||
return this.resourceService.getResourceMeta(tenantId, resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates if the resource metadata is exportable.
|
|
||||||
* @param {any} resourceMeta - The metadata of the resource.
|
|
||||||
* @throws {ServiceError} If the resource is not exportable or lacks columns.
|
|
||||||
*/
|
|
||||||
private validateResourceMeta(resourceMeta: any) {
|
|
||||||
if (!resourceMeta.exportable || !resourceMeta.columns) {
|
|
||||||
throw new ServiceError(Errors.RESOURCE_NOT_EXPORTABLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transforms the exported data based on the resource metadata.
|
|
||||||
* If the resource metadata specifies a flattening attribute (`exportFlattenOn`),
|
|
||||||
* the data will be flattened based on this attribute using the `flatDataCollections` utility function.
|
|
||||||
*
|
|
||||||
* @param {number} tenantId - The tenant identifier.
|
|
||||||
* @param {string} resource - The name of the resource.
|
|
||||||
* @param {Array<Record<string, any>>} data - The original data to be transformed.
|
|
||||||
* @returns {Array<Record<string, any>>} - The transformed data.
|
|
||||||
*/
|
|
||||||
private transformExportedData(
|
|
||||||
tenantId: number,
|
|
||||||
resource: string,
|
|
||||||
data: Array<Record<string, any>>
|
|
||||||
): Array<Record<string, any>> {
|
|
||||||
const resourceMeta = this.getResourceMeta(tenantId, resource);
|
|
||||||
|
|
||||||
return R.when<Array<Record<string, any>>, Array<Record<string, any>>>(
|
|
||||||
R.always(Boolean(resourceMeta.exportFlattenOn)),
|
|
||||||
(data) => flatDataCollections(data, resourceMeta.exportFlattenOn),
|
|
||||||
data
|
|
||||||
);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Fetches exportable data for a given resource.
|
|
||||||
* @param {number} tenantId - The tenant identifier.
|
|
||||||
* @param {string} resource - The name of the resource.
|
|
||||||
* @returns A promise that resolves to the exportable data.
|
|
||||||
*/
|
|
||||||
private async getExportableData(tenantId: number, resource: string) {
|
|
||||||
const exportable =
|
|
||||||
this.exportableResources.registry.getExportable(resource);
|
|
||||||
return exportable.exportable(tenantId, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extracts columns that are marked as exportable from the resource metadata.
|
|
||||||
* @param {IModelMeta} resourceMeta - The metadata of the resource.
|
|
||||||
* @returns An array of exportable columns.
|
|
||||||
*/
|
|
||||||
private getExportableColumns(resourceMeta: IModelMeta) {
|
|
||||||
const processColumns = (
|
|
||||||
columns: { [key: string]: IModelMetaColumn },
|
|
||||||
parent = ''
|
|
||||||
) => {
|
|
||||||
return Object.entries(columns)
|
|
||||||
.filter(([_, value]) => value.exportable !== false)
|
|
||||||
.flatMap(([key, value]) => {
|
|
||||||
if (value.type === 'collection' && value.collectionOf === 'object') {
|
|
||||||
return processColumns(value.columns, key);
|
|
||||||
} else {
|
|
||||||
const group = parent;
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: value.name,
|
|
||||||
type: value.type || 'text',
|
|
||||||
accessor: value.accessor || key,
|
|
||||||
group,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
return processColumns(resourceMeta.columns);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a workbook from the provided data and columns.
|
|
||||||
* @param {any[]} data - The data to be included in the workbook.
|
|
||||||
* @param {any[]} exportableColumns - The columns to be included in the workbook.
|
|
||||||
* @returns The created workbook.
|
|
||||||
*/
|
|
||||||
private createWorkbook(data: any[], exportableColumns: any[]) {
|
|
||||||
const workbook = xlsx.utils.book_new();
|
|
||||||
const worksheetData = data.map((item) =>
|
|
||||||
exportableColumns.map((col) => get(item, getDataAccessor(col)))
|
|
||||||
);
|
|
||||||
|
|
||||||
worksheetData.unshift(exportableColumns.map((col) => col.name));
|
|
||||||
|
|
||||||
const worksheet = xlsx.utils.aoa_to_sheet(worksheetData);
|
|
||||||
xlsx.utils.book_append_sheet(workbook, worksheet, 'Exported Data');
|
|
||||||
|
|
||||||
return workbook;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exports the workbook in the specified format.
|
|
||||||
* @param {any} workbook - The workbook to be exported.
|
|
||||||
* @param {string} format - The format to export the workbook in.
|
|
||||||
* @returns The exported workbook data.
|
|
||||||
*/
|
|
||||||
private exportWorkbook(workbook: any, format: string) {
|
|
||||||
if (format.toLowerCase() === 'csv') {
|
|
||||||
return xlsx.write(workbook, { type: 'buffer', bookType: 'csv' });
|
|
||||||
} else if (format.toLowerCase() === 'xlsx') {
|
|
||||||
return xlsx.write(workbook, { type: 'buffer', bookType: 'xlsx' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
export class Exportable {
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param tenantId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public async exportable(
|
|
||||||
tenantId: number,
|
|
||||||
query: Record<string, any>
|
|
||||||
): Promise<Array<Record<string, any>>> {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param data
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public transform(data: Record<string, any>) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export enum Errors {
|
|
||||||
RESOURCE_NOT_EXPORTABLE = 'RESOURCE_NOT_EXPORTABLE',
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import { flatMap } from 'lodash';
|
|
||||||
/**
|
|
||||||
* Flattens the data based on a specified attribute.
|
|
||||||
* @param data - The data to be flattened.
|
|
||||||
* @param flattenAttr - The attribute to be flattened.
|
|
||||||
* @returns - The flattened data.
|
|
||||||
*/
|
|
||||||
export const flatDataCollections = (
|
|
||||||
data: Record<string, any>,
|
|
||||||
flattenAttr: string
|
|
||||||
): Record<string, any>[] => {
|
|
||||||
return flatMap(data, (item) =>
|
|
||||||
item[flattenAttr].map((entry) => ({
|
|
||||||
...item,
|
|
||||||
[flattenAttr]: entry,
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the data accessor for a given column.
|
|
||||||
* @param col - The column to get the data accessor for.
|
|
||||||
* @returns - The data accessor.
|
|
||||||
*/
|
|
||||||
export const getDataAccessor = (col: any) => {
|
|
||||||
return col.group ? `${col.group}.${col.accessor}` : col.accessor;
|
|
||||||
};
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { Exportable } from '../Export/Exportable';
|
|
||||||
import { IAccountsFilter, IAccountsStructureType } from '@/interfaces';
|
|
||||||
import ItemCategoriesService from './ItemCategoriesService';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class ItemCategoriesExportable extends Exportable {
|
|
||||||
@Inject()
|
|
||||||
private itemCategoriesApplication: ItemCategoriesService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the accounts data to exportable sheet.
|
|
||||||
* @param {number} tenantId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public exportable(tenantId: number, query: IAccountsFilter) {
|
|
||||||
const parsedQuery = {
|
|
||||||
sortOrder: 'desc',
|
|
||||||
columnSortBy: 'created_at',
|
|
||||||
inactiveMode: false,
|
|
||||||
...query,
|
|
||||||
structure: IAccountsStructureType.Flat,
|
|
||||||
} as IAccountsFilter;
|
|
||||||
|
|
||||||
return this.itemCategoriesApplication
|
|
||||||
.getItemCategoriesList(tenantId, parsedQuery, {})
|
|
||||||
.then((output) => output.itemCategories);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { Exportable } from '../Export/Exportable';
|
|
||||||
import { IItemsFilter } from '@/interfaces';
|
|
||||||
import { ItemsApplication } from './ItemsApplication';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class ItemsExportable extends Exportable {
|
|
||||||
@Inject()
|
|
||||||
private itemsApplication: ItemsApplication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the accounts data to exportable sheet.
|
|
||||||
* @param {number} tenantId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public exportable(tenantId: number, query: IItemsFilter) {
|
|
||||||
const parsedQuery = {
|
|
||||||
sortOrder: 'DESC',
|
|
||||||
columnSortBy: 'created_at',
|
|
||||||
page: 1,
|
|
||||||
...query,
|
|
||||||
pageSize: 12,
|
|
||||||
} as IItemsFilter;
|
|
||||||
|
|
||||||
return this.itemsApplication
|
|
||||||
.getItems(tenantId, parsedQuery)
|
|
||||||
.then((output) => output.items);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -39,7 +39,7 @@ export class GetManualJournals {
|
|||||||
tenantId: number,
|
tenantId: number,
|
||||||
filterDTO: IManualJournalsFilter
|
filterDTO: IManualJournalsFilter
|
||||||
): Promise<{
|
): Promise<{
|
||||||
manualJournals: IManualJournal[];
|
manualJournals: IManualJournal;
|
||||||
pagination: IPaginationMeta;
|
pagination: IPaginationMeta;
|
||||||
filterMeta: IFilterMeta;
|
filterMeta: IFilterMeta;
|
||||||
}> => {
|
}> => {
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { IManualJournalsFilter } from '@/interfaces';
|
|
||||||
import { Exportable } from '../Export/Exportable';
|
|
||||||
import { ManualJournalsApplication } from './ManualJournalsApplication';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class ManualJournalsExportable extends Exportable {
|
|
||||||
@Inject()
|
|
||||||
private manualJournalsApplication: ManualJournalsApplication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the manual journals data to exportable sheet.
|
|
||||||
* @param {number} tenantId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public exportable(tenantId: number, query: IManualJournalsFilter) {
|
|
||||||
const parsedQuery = {
|
|
||||||
sortOrder: 'desc',
|
|
||||||
columnSortBy: 'created_at',
|
|
||||||
...query,
|
|
||||||
page: 1,
|
|
||||||
pageSize: 12000,
|
|
||||||
} as IManualJournalsFilter;
|
|
||||||
|
|
||||||
return this.manualJournalsApplication
|
|
||||||
.getManualJournals(tenantId, parsedQuery)
|
|
||||||
.then((output) => output.manualJournals);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { Exportable } from '@/services/Export/Exportable';
|
|
||||||
import { BillPaymentsApplication } from './BillPaymentsApplication';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class BillPaymentExportable extends Exportable {
|
|
||||||
@Inject()
|
|
||||||
private billPaymentsApplication: BillPaymentsApplication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the accounts data to exportable sheet.
|
|
||||||
* @param {number} tenantId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public exportable(tenantId: number, query: any) {
|
|
||||||
const parsedQuery = {
|
|
||||||
page: 1,
|
|
||||||
pageSize: 12,
|
|
||||||
...query,
|
|
||||||
sortOrder: 'desc',
|
|
||||||
columnSortBy: 'created_at',
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
return this.billPaymentsApplication
|
|
||||||
.getBillPayments(tenantId, parsedQuery)
|
|
||||||
.then((output) => output.billPayments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -31,7 +31,7 @@ export class GetBillPayments {
|
|||||||
tenantId: number,
|
tenantId: number,
|
||||||
filterDTO: IBillPaymentsFilter
|
filterDTO: IBillPaymentsFilter
|
||||||
): Promise<{
|
): Promise<{
|
||||||
billPayments: IBillPayment[];
|
billPayments: IBillPayment;
|
||||||
pagination: IPaginationMeta;
|
pagination: IPaginationMeta;
|
||||||
filterMeta: IFilterMeta;
|
filterMeta: IFilterMeta;
|
||||||
}> {
|
}> {
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ export class BillsApplication {
|
|||||||
tenantId: number,
|
tenantId: number,
|
||||||
filterDTO: IBillsFilter
|
filterDTO: IBillsFilter
|
||||||
): Promise<{
|
): Promise<{
|
||||||
bills: IBill[];
|
bills: IBill;
|
||||||
pagination: IPaginationMeta;
|
pagination: IPaginationMeta;
|
||||||
filterMeta: IFilterMeta;
|
filterMeta: IFilterMeta;
|
||||||
}> {
|
}> {
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { IBillsFilter } from '@/interfaces';
|
|
||||||
import { Exportable } from '@/services/Export/Exportable';
|
|
||||||
import { BillsApplication } from './BillsApplication';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class BillsExportable extends Exportable {
|
|
||||||
@Inject()
|
|
||||||
private billsApplication: BillsApplication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the accounts data to exportable sheet.
|
|
||||||
* @param {number} tenantId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public exportable(tenantId: number, query: IBillsFilter) {
|
|
||||||
const parsedQuery = {
|
|
||||||
sortOrder: 'desc',
|
|
||||||
columnSortBy: 'created_at',
|
|
||||||
...query,
|
|
||||||
page: 1,
|
|
||||||
pageSize: 12000,
|
|
||||||
} as IBillsFilter;
|
|
||||||
|
|
||||||
return this.billsApplication
|
|
||||||
.getBills(tenantId, parsedQuery)
|
|
||||||
.then((output) => output.bills);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -49,7 +49,6 @@ export class GetBills {
|
|||||||
const { results, pagination } = await Bill.query()
|
const { results, pagination } = await Bill.query()
|
||||||
.onBuild((builder) => {
|
.onBuild((builder) => {
|
||||||
builder.withGraphFetched('vendor');
|
builder.withGraphFetched('vendor');
|
||||||
builder.withGraphFetched('entries.item');
|
|
||||||
dynamicFilter.buildQuery()(builder);
|
dynamicFilter.buildQuery()(builder);
|
||||||
})
|
})
|
||||||
.pagination(filter.page - 1, filter.pageSize);
|
.pagination(filter.page - 1, filter.pageSize);
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ export class VendorCreditTransformer extends Transformer {
|
|||||||
'formattedSubtotal',
|
'formattedSubtotal',
|
||||||
'formattedVendorCreditDate',
|
'formattedVendorCreditDate',
|
||||||
'formattedCreditsRemaining',
|
'formattedCreditsRemaining',
|
||||||
'formattedInvoicedAmount',
|
|
||||||
'entries',
|
'entries',
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -59,17 +58,6 @@ export class VendorCreditTransformer extends Transformer {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the formatted invoiced amount.
|
|
||||||
* @param credit
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
protected formattedInvoicedAmount = (credit) => {
|
|
||||||
return formatNumber(credit.invoicedAmount, {
|
|
||||||
currencyCode: credit.currencyCode,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the entries of the bill.
|
* Retrieves the entries of the bill.
|
||||||
* @param {IVendorCredit} vendorCredit
|
* @param {IVendorCredit} vendorCredit
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { IVendorCreditsQueryDTO } from '@/interfaces';
|
|
||||||
import ListVendorCredits from './ListVendorCredits';
|
|
||||||
import { Exportable } from '@/services/Export/Exportable';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class VendorCreditsExportable extends Exportable {
|
|
||||||
@Inject()
|
|
||||||
private getVendorCredits: ListVendorCredits;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the vendor credits data to exportable sheet.
|
|
||||||
* @param {number} tenantId -
|
|
||||||
* @param {IVendorCreditsQueryDTO} query -
|
|
||||||
* @returns {}
|
|
||||||
*/
|
|
||||||
public exportable(tenantId: number, query: IVendorCreditsQueryDTO) {
|
|
||||||
const parsedQuery = {
|
|
||||||
sortOrder: 'desc',
|
|
||||||
columnSortBy: 'created_at',
|
|
||||||
...query,
|
|
||||||
page: 1,
|
|
||||||
pageSize: 12000,
|
|
||||||
} as IVendorCreditsQueryDTO;
|
|
||||||
|
|
||||||
return this.getVendorCredits
|
|
||||||
.getVendorCredits(tenantId, parsedQuery)
|
|
||||||
.then((output) => output.vendorCredits);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -105,11 +105,7 @@ export default class ResourceService {
|
|||||||
const $enumerationType = (field) =>
|
const $enumerationType = (field) =>
|
||||||
field.fieldType === 'enumeration' ? field : undefined;
|
field.fieldType === 'enumeration' ? field : undefined;
|
||||||
|
|
||||||
const $hasFields = (field) =>
|
const $hasFields = (field) => 'undefined' !== typeof field.fields ? field : undefined;
|
||||||
'undefined' !== typeof field.fields ? field : undefined;
|
|
||||||
|
|
||||||
const $hasColumns = (column) =>
|
|
||||||
'undefined' !== typeof column.columns ? column : undefined;
|
|
||||||
|
|
||||||
const naviagations = [
|
const naviagations = [
|
||||||
['fields', qim.$each, 'name'],
|
['fields', qim.$each, 'name'],
|
||||||
@@ -117,8 +113,6 @@ export default class ResourceService {
|
|||||||
['fields2', qim.$each, 'name'],
|
['fields2', qim.$each, 'name'],
|
||||||
['fields2', qim.$each, $enumerationType, 'options', qim.$each, 'label'],
|
['fields2', qim.$each, $enumerationType, 'options', qim.$each, 'label'],
|
||||||
['fields2', qim.$each, $hasFields, 'fields', qim.$each, 'name'],
|
['fields2', qim.$each, $hasFields, 'fields', qim.$each, 'name'],
|
||||||
['columns', qim.$each, 'name'],
|
|
||||||
['columns', qim.$each, $hasColumns, 'columns', qim.$each, 'name'],
|
|
||||||
];
|
];
|
||||||
return this.i18nService.i18nApply(naviagations, meta, tenantId);
|
return this.i18nService.i18nApply(naviagations, meta, tenantId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ export class GetSaleEstimates {
|
|||||||
.onBuild((builder) => {
|
.onBuild((builder) => {
|
||||||
builder.withGraphFetched('customer');
|
builder.withGraphFetched('customer');
|
||||||
builder.withGraphFetched('entries');
|
builder.withGraphFetched('entries');
|
||||||
builder.withGraphFetched('entries.item');
|
|
||||||
dynamicFilter.buildQuery()(builder);
|
dynamicFilter.buildQuery()(builder);
|
||||||
})
|
})
|
||||||
.pagination(filter.page - 1, filter.pageSize);
|
.pagination(filter.page - 1, filter.pageSize);
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { ISalesInvoicesFilter } from '@/interfaces';
|
|
||||||
import { Exportable } from '@/services/Export/Exportable';
|
|
||||||
import { SaleEstimatesApplication } from './SaleEstimatesApplication';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class SaleEstimatesExportable extends Exportable {
|
|
||||||
@Inject()
|
|
||||||
private saleEstimatesApplication: SaleEstimatesApplication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the accounts data to exportable sheet.
|
|
||||||
* @param {number} tenantId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public exportable(tenantId: number, query: ISalesInvoicesFilter) {
|
|
||||||
const parsedQuery = {
|
|
||||||
sortOrder: 'desc',
|
|
||||||
columnSortBy: 'created_at',
|
|
||||||
...query,
|
|
||||||
page: 1,
|
|
||||||
pageSize: 12000,
|
|
||||||
} as ISalesInvoicesFilter;
|
|
||||||
|
|
||||||
return this.saleEstimatesApplication
|
|
||||||
.getSaleEstimates(tenantId, parsedQuery)
|
|
||||||
.then((output) => output.salesEstimates);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -49,7 +49,7 @@ export class GetSaleInvoices {
|
|||||||
);
|
);
|
||||||
const { results, pagination } = await SaleInvoice.query()
|
const { results, pagination } = await SaleInvoice.query()
|
||||||
.onBuild((builder) => {
|
.onBuild((builder) => {
|
||||||
builder.withGraphFetched('entries.item');
|
builder.withGraphFetched('entries');
|
||||||
builder.withGraphFetched('customer');
|
builder.withGraphFetched('customer');
|
||||||
dynamicFilter.buildQuery()(builder);
|
dynamicFilter.buildQuery()(builder);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { ISalesInvoicesFilter } from '@/interfaces';
|
|
||||||
import { SaleInvoiceApplication } from './SaleInvoicesApplication';
|
|
||||||
import { Exportable } from '@/services/Export/Exportable';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class SaleInvoicesExportable extends Exportable {
|
|
||||||
@Inject()
|
|
||||||
private saleInvoicesApplication: SaleInvoiceApplication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the accounts data to exportable sheet.
|
|
||||||
* @param {number} tenantId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public exportable(tenantId: number, query: ISalesInvoicesFilter) {
|
|
||||||
const parsedQuery = {
|
|
||||||
sortOrder: 'desc',
|
|
||||||
columnSortBy: 'created_at',
|
|
||||||
...query,
|
|
||||||
page: 1,
|
|
||||||
pageSize: 120000,
|
|
||||||
} as ISalesInvoicesFilter;
|
|
||||||
|
|
||||||
return this.saleInvoicesApplication
|
|
||||||
.getSaleInvoices(tenantId, parsedQuery)
|
|
||||||
.then((output) => output.salesInvoices);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { IAccountsStructureType, IPaymentReceivesFilter } from '@/interfaces';
|
|
||||||
import { Exportable } from '@/services/Export/Exportable';
|
|
||||||
import { PaymentReceivesApplication } from './PaymentReceivesApplication';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class PaymentsReceivedExportable extends Exportable {
|
|
||||||
@Inject()
|
|
||||||
private paymentReceivedApp: PaymentReceivesApplication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the accounts data to exportable sheet.
|
|
||||||
* @param {number} tenantId
|
|
||||||
* @param {IPaymentReceivesFilter} query -
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public exportable(tenantId: number, query: IPaymentReceivesFilter) {
|
|
||||||
const parsedQuery = {
|
|
||||||
sortOrder: 'desc',
|
|
||||||
columnSortBy: 'created_at',
|
|
||||||
inactiveMode: false,
|
|
||||||
...query,
|
|
||||||
structure: IAccountsStructureType.Flat,
|
|
||||||
} as IPaymentReceivesFilter;
|
|
||||||
|
|
||||||
return this.paymentReceivedApp
|
|
||||||
.getPaymentReceives(tenantId, parsedQuery)
|
|
||||||
.then((output) => output.paymentReceives);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,9 +11,6 @@ import { SaleReceiptTransformer } from './SaleReceiptTransformer';
|
|||||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
|
||||||
|
|
||||||
interface GetSaleReceiptsSettings {
|
|
||||||
fetchEntriesGraph?: boolean;
|
|
||||||
}
|
|
||||||
@Service()
|
@Service()
|
||||||
export class GetSaleReceipts {
|
export class GetSaleReceipts {
|
||||||
@Inject()
|
@Inject()
|
||||||
@@ -53,7 +50,7 @@ export class GetSaleReceipts {
|
|||||||
.onBuild((builder) => {
|
.onBuild((builder) => {
|
||||||
builder.withGraphFetched('depositAccount');
|
builder.withGraphFetched('depositAccount');
|
||||||
builder.withGraphFetched('customer');
|
builder.withGraphFetched('customer');
|
||||||
builder.withGraphFetched('entries.item');
|
builder.withGraphFetched('entries');
|
||||||
|
|
||||||
dynamicFilter.buildQuery()(builder);
|
dynamicFilter.buildQuery()(builder);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { ISalesReceiptsFilter } from '@/interfaces';
|
|
||||||
import { Exportable } from '@/services/Export/Exportable';
|
|
||||||
import { SaleReceiptApplication } from './SaleReceiptApplication';
|
|
||||||
|
|
||||||
@Service()
|
|
||||||
export class SaleReceiptsExportable extends Exportable {
|
|
||||||
@Inject()
|
|
||||||
private saleReceiptsApp: SaleReceiptApplication;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the accounts data to exportable sheet.
|
|
||||||
* @param {number} tenantId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
public exportable(tenantId: number, query: ISalesReceiptsFilter) {
|
|
||||||
const parsedQuery = {
|
|
||||||
sortOrder: 'desc',
|
|
||||||
columnSortBy: 'created_at',
|
|
||||||
...query,
|
|
||||||
page: 1,
|
|
||||||
pageSize: 12,
|
|
||||||
} as ISalesReceiptsFilter;
|
|
||||||
|
|
||||||
return this.saleReceiptsApp
|
|
||||||
.getSaleReceipts(tenantId, parsedQuery)
|
|
||||||
.then((output) => output.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,9 +9,6 @@ export default {
|
|||||||
signUp: 'onSignUp',
|
signUp: 'onSignUp',
|
||||||
signingUp: 'onSigningUp',
|
signingUp: 'onSigningUp',
|
||||||
|
|
||||||
signUpConfirming: 'signUpConfirming',
|
|
||||||
signUpConfirmed: 'signUpConfirmed',
|
|
||||||
|
|
||||||
sendingResetPassword: 'onSendingResetPassword',
|
sendingResetPassword: 'onSendingResetPassword',
|
||||||
sendResetPassword: 'onSendResetPassword',
|
sendResetPassword: 'onSendResetPassword',
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
exports.up = function (knex) {
|
|
||||||
return knex.schema
|
|
||||||
.table('users', (table) => {
|
|
||||||
table.string('verify_token');
|
|
||||||
table.boolean('verified').defaultTo(false);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
return knex('USERS').update({ verified: true });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.down = (knex) => {};
|
|
||||||
@@ -4,12 +4,6 @@ import SystemModel from '@/system/models/SystemModel';
|
|||||||
import SoftDeleteQueryBuilder from '@/collection/SoftDeleteQueryBuilder';
|
import SoftDeleteQueryBuilder from '@/collection/SoftDeleteQueryBuilder';
|
||||||
|
|
||||||
export default class SystemUser extends SystemModel {
|
export default class SystemUser extends SystemModel {
|
||||||
firstName!: string;
|
|
||||||
lastName!: string;
|
|
||||||
verified!: boolean;
|
|
||||||
inviteAcceptedAt!: Date | null;
|
|
||||||
deletedAt!: Date | null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table name.
|
* Table name.
|
||||||
*/
|
*/
|
||||||
@@ -35,33 +29,23 @@ export default class SystemUser extends SystemModel {
|
|||||||
* Virtual attributes.
|
* Virtual attributes.
|
||||||
*/
|
*/
|
||||||
static get virtualAttributes() {
|
static get virtualAttributes() {
|
||||||
return ['fullName', 'isDeleted', 'isInviteAccepted', 'isVerified'];
|
return ['fullName', 'isDeleted', 'isInviteAccepted'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detarmines whether the user is deleted.
|
*
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
*/
|
||||||
get isDeleted() {
|
get isDeleted() {
|
||||||
return !!this.deletedAt;
|
return !!this.deletedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detarmines whether the sent invite is accepted.
|
*
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
*/
|
||||||
get isInviteAccepted() {
|
get isInviteAccepted() {
|
||||||
return !!this.inviteAcceptedAt;
|
return !!this.inviteAcceptedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Detarmines whether the user's email is verified.
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
get isVerified() {
|
|
||||||
return !!this.verified;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Full name attribute.
|
* Full name attribute.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import _, { isEmpty } from 'lodash';
|
import _ from 'lodash';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
|
|
||||||
@@ -329,7 +329,7 @@ const booleanValuesRepresentingTrue: string[] = ['true', '1'];
|
|||||||
const booleanValuesRepresentingFalse: string[] = ['false', '0'];
|
const booleanValuesRepresentingFalse: string[] = ['false', '0'];
|
||||||
|
|
||||||
const normalizeValue = (value: any): string =>
|
const normalizeValue = (value: any): string =>
|
||||||
value?.toString().trim().toLowerCase();
|
value.toString().trim().toLowerCase();
|
||||||
|
|
||||||
const booleanValues: string[] = [
|
const booleanValues: string[] = [
|
||||||
...booleanValuesRepresentingTrue,
|
...booleanValuesRepresentingTrue,
|
||||||
@@ -338,7 +338,7 @@ const booleanValues: string[] = [
|
|||||||
|
|
||||||
export const parseBoolean = <T>(value: any, defaultValue: T): T | boolean => {
|
export const parseBoolean = <T>(value: any, defaultValue: T): T | boolean => {
|
||||||
const normalizedValue = normalizeValue(value);
|
const normalizedValue = normalizeValue(value);
|
||||||
if (isEmpty(value) || booleanValues.indexOf(normalizedValue) === -1) {
|
if (booleanValues.indexOf(normalizedValue) === -1) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
return booleanValuesRepresentingTrue.indexOf(normalizedValue) !== -1;
|
return booleanValuesRepresentingTrue.indexOf(normalizedValue) !== -1;
|
||||||
|
|||||||
@@ -1,424 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width" />
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
||||||
<title>Bigcapital | Reset your password</title>
|
|
||||||
<style>
|
|
||||||
/* -------------------------------------
|
|
||||||
GLOBAL RESETS
|
|
||||||
------------------------------------- */
|
|
||||||
|
|
||||||
/*All the styling goes here*/
|
|
||||||
|
|
||||||
img {
|
|
||||||
border: none;
|
|
||||||
-ms-interpolation-mode: bicubic;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
font-family: sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
-ms-text-size-adjust: 100%;
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: separate;
|
|
||||||
mso-table-lspace: 0pt;
|
|
||||||
mso-table-rspace: 0pt;
|
|
||||||
width: 100%; }
|
|
||||||
table td {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
BODY & CONTAINER
|
|
||||||
------------------------------------- */
|
|
||||||
|
|
||||||
.body {
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
|
||||||
.container {
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto !important;
|
|
||||||
/* makes it centered */
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px;
|
|
||||||
width: 580px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
|
||||||
.content {
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 580px;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
HEADER, FOOTER, MAIN
|
|
||||||
------------------------------------- */
|
|
||||||
.main {
|
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 3px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-block {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
clear: both;
|
|
||||||
margin-top: 10px;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.footer td,
|
|
||||||
.footer p,
|
|
||||||
.footer span,
|
|
||||||
.footer a {
|
|
||||||
color: #999999;
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
TYPOGRAPHY
|
|
||||||
------------------------------------- */
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4 {
|
|
||||||
color: #000000;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-weight: bold;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 35px;
|
|
||||||
font-weight: 300;
|
|
||||||
text-align: center;
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
p,
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 0;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
p li,
|
|
||||||
ul li,
|
|
||||||
ol li {
|
|
||||||
list-style-position: inside;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #3498db;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
BUTTONS
|
|
||||||
------------------------------------- */
|
|
||||||
.btn {
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
}
|
|
||||||
.btn > tbody > tr > td {
|
|
||||||
padding-bottom: 15px; }
|
|
||||||
.btn table {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
.btn table td {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.btn a {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: solid 1px #3498db;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: #3498db;
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 0;
|
|
||||||
padding: 12px 25px;
|
|
||||||
text-decoration: none;
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary table td {
|
|
||||||
background-color: #2d95fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary a {
|
|
||||||
background-color: #1968F0;
|
|
||||||
border-color: #1968F0;
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
OTHER STYLES THAT MIGHT BE USEFUL
|
|
||||||
------------------------------------- */
|
|
||||||
.last {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.first {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-center {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-right {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-left {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clear {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mt0 {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mb0 {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mb4{
|
|
||||||
margin-bottom: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preheader {
|
|
||||||
color: transparent;
|
|
||||||
display: none;
|
|
||||||
height: 0;
|
|
||||||
max-height: 0;
|
|
||||||
max-width: 0;
|
|
||||||
opacity: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
mso-hide: all;
|
|
||||||
visibility: hidden;
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.powered-by a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 1px solid #f6f6f6;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
|
||||||
------------------------------------- */
|
|
||||||
@media only screen and (max-width: 620px) {
|
|
||||||
table[class=body] h1 {
|
|
||||||
font-size: 28px !important;
|
|
||||||
margin-bottom: 10px !important;
|
|
||||||
}
|
|
||||||
table[class=body] p,
|
|
||||||
table[class=body] ul,
|
|
||||||
table[class=body] ol,
|
|
||||||
table[class=body] td,
|
|
||||||
table[class=body] span,
|
|
||||||
table[class=body] a {
|
|
||||||
font-size: 16px !important;
|
|
||||||
}
|
|
||||||
table[class=body] .wrapper,
|
|
||||||
table[class=body] .article {
|
|
||||||
padding: 10px !important;
|
|
||||||
}
|
|
||||||
table[class=body] .content {
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
table[class=body] .container {
|
|
||||||
padding: 0 !important;
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
table[class=body] .main {
|
|
||||||
border-left-width: 0 !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
border-right-width: 0 !important;
|
|
||||||
}
|
|
||||||
table[class=body] .btn table {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
table[class=body] .btn a {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
table[class=body] .img-responsive {
|
|
||||||
height: auto !important;
|
|
||||||
max-width: 100% !important;
|
|
||||||
width: auto !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------------
|
|
||||||
PRESERVE THESE STYLES IN THE HEAD
|
|
||||||
------------------------------------- */
|
|
||||||
@media all {
|
|
||||||
.ExternalClass {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.ExternalClass,
|
|
||||||
.ExternalClass p,
|
|
||||||
.ExternalClass span,
|
|
||||||
.ExternalClass font,
|
|
||||||
.ExternalClass td,
|
|
||||||
.ExternalClass div {
|
|
||||||
line-height: 100%;
|
|
||||||
}
|
|
||||||
.apple-link a {
|
|
||||||
color: inherit !important;
|
|
||||||
font-family: inherit !important;
|
|
||||||
font-size: inherit !important;
|
|
||||||
font-weight: inherit !important;
|
|
||||||
line-height: inherit !important;
|
|
||||||
text-decoration: none !important;
|
|
||||||
}
|
|
||||||
#MessageViewBody a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: inherit;
|
|
||||||
font-family: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
|
||||||
.btn-primary table td:hover {
|
|
||||||
background-color: #004dd0 !important;
|
|
||||||
}
|
|
||||||
.btn-primary a:hover {
|
|
||||||
background-color: #004dd0 !important;
|
|
||||||
border-color: #004dd0 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[data-icon="bigcapital"] path {
|
|
||||||
fill: #004dd0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-icon='bigcapital'] .path-1,
|
|
||||||
[data-icon='bigcapital'] .path-13 {
|
|
||||||
fill: #2d95fd;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body class="">
|
|
||||||
<span class="preheader">Verify your email.</span>
|
|
||||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body">
|
|
||||||
<tr>
|
|
||||||
<td> </td>
|
|
||||||
<td class="container">
|
|
||||||
<div class="content">
|
|
||||||
|
|
||||||
<!-- START CENTERED WHITE CONTAINER -->
|
|
||||||
<table role="presentation" class="main">
|
|
||||||
<!-- START MAIN CONTENT AREA -->
|
|
||||||
<tr>
|
|
||||||
<td class="wrapper">
|
|
||||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p class="align-center">
|
|
||||||
<img src="cid:bigcapital_logo" />
|
|
||||||
</p>
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<p class="align-center">
|
|
||||||
<h2>Verify your email</h2>
|
|
||||||
</p>
|
|
||||||
<p class="mgb-1x">Hi <strong>{{ fullName }}<strong>,</p>
|
|
||||||
<p class="mgb-2-5x">To continue setting up your Bigcapital account, please verify that this is your email address.</p>
|
|
||||||
|
|
||||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td align="left">
|
|
||||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td> <a href="{{ verifyUrl }}" target="_blank">Verify email address</a> </td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<p>If this was a mistake, just ignore this email and nothing will happen.</p>
|
|
||||||
<p class="email-note">This is an automatically generated email please do not reply to this email.</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<!-- END MAIN CONTENT AREA -->
|
|
||||||
</table>
|
|
||||||
<!-- END CENTERED WHITE CONTAINER -->
|
|
||||||
|
|
||||||
<!-- START FOOTER -->
|
|
||||||
<div class="footer">
|
|
||||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
|
||||||
<tr>
|
|
||||||
<td class="content-block powered-by">
|
|
||||||
Powered by <a href="https://Bigcapital.ly">Bigcapital.ly</a>.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- END FOOTER -->
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -7,8 +7,8 @@ WORKDIR /app
|
|||||||
# Copy application dependency manifests to the container image.
|
# Copy application dependency manifests to the container image.
|
||||||
COPY ./package*.json ./
|
COPY ./package*.json ./
|
||||||
COPY ./pnpm-lock.yaml ./pnpm-lock.yaml
|
COPY ./pnpm-lock.yaml ./pnpm-lock.yaml
|
||||||
COPY ./lerna.json ./lerna.json
|
|
||||||
COPY ./pnpm-workspace.yaml ./pnpm-workspace.yaml
|
COPY ./pnpm-workspace.yaml ./pnpm-workspace.yaml
|
||||||
|
COPY ./lerna.json ./lerna.json
|
||||||
COPY ./packages/webapp/package*.json ./packages/webapp/
|
COPY ./packages/webapp/package*.json ./packages/webapp/
|
||||||
|
|
||||||
# Install application dependencies
|
# Install application dependencies
|
||||||
@@ -24,7 +24,7 @@ RUN pnpm install
|
|||||||
|
|
||||||
# Build webapp package
|
# Build webapp package
|
||||||
COPY ./packages/webapp /app/packages/webapp
|
COPY ./packages/webapp /app/packages/webapp
|
||||||
RUN pnpm run build:webapp
|
RUN npm run build:webapp
|
||||||
|
|
||||||
FROM nginx
|
FROM nginx
|
||||||
|
|
||||||
|
|||||||
@@ -9,24 +9,13 @@ import 'moment/locale/ar-ly';
|
|||||||
import 'moment/locale/es-us';
|
import 'moment/locale/es-us';
|
||||||
|
|
||||||
import AppIntlLoader from './AppIntlLoader';
|
import AppIntlLoader from './AppIntlLoader';
|
||||||
import { EnsureAuthenticated } from '@/components/Guards/EnsureAuthenticated';
|
import PrivateRoute from '@/components/Guards/PrivateRoute';
|
||||||
import GlobalErrors from '@/containers/GlobalErrors/GlobalErrors';
|
import GlobalErrors from '@/containers/GlobalErrors/GlobalErrors';
|
||||||
import DashboardPrivatePages from '@/components/Dashboard/PrivatePages';
|
import DashboardPrivatePages from '@/components/Dashboard/PrivatePages';
|
||||||
import { Authentication } from '@/containers/Authentication/Authentication';
|
import { Authentication } from '@/containers/Authentication/Authentication';
|
||||||
|
|
||||||
import LazyLoader from '@/components/LazyLoader';
|
|
||||||
import { SplashScreen, DashboardThemeProvider } from '../components';
|
import { SplashScreen, DashboardThemeProvider } from '../components';
|
||||||
import { queryConfig } from '../hooks/query/base';
|
import { queryConfig } from '../hooks/query/base';
|
||||||
import { EnsureUserEmailVerified } from './Guards/EnsureUserEmailVerified';
|
|
||||||
import { EnsureAuthNotAuthenticated } from './Guards/EnsureAuthNotAuthenticated';
|
|
||||||
import { EnsureUserEmailNotVerified } from './Guards/EnsureUserEmailNotVerified';
|
|
||||||
|
|
||||||
const EmailConfirmation = LazyLoader({
|
|
||||||
loader: () => import('@/containers/Authentication/EmailConfirmation'),
|
|
||||||
});
|
|
||||||
const RegisterVerify = LazyLoader({
|
|
||||||
loader: () => import('@/containers/Authentication/RegisterVerify'),
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* App inner.
|
* App inner.
|
||||||
@@ -37,30 +26,9 @@ function AppInsider({ history }) {
|
|||||||
<DashboardThemeProvider>
|
<DashboardThemeProvider>
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path={'/auth/register/verify'}>
|
<Route path={'/auth'} component={Authentication} />
|
||||||
<EnsureAuthenticated>
|
|
||||||
<EnsureUserEmailNotVerified>
|
|
||||||
<RegisterVerify />
|
|
||||||
</EnsureUserEmailNotVerified>
|
|
||||||
</EnsureAuthenticated>
|
|
||||||
</Route>
|
|
||||||
|
|
||||||
<Route path={'/auth/email_confirmation'}>
|
|
||||||
<EmailConfirmation />
|
|
||||||
</Route>
|
|
||||||
|
|
||||||
<Route path={'/auth'}>
|
|
||||||
<EnsureAuthNotAuthenticated>
|
|
||||||
<Authentication />
|
|
||||||
</EnsureAuthNotAuthenticated>
|
|
||||||
</Route>
|
|
||||||
|
|
||||||
<Route path={'/'}>
|
<Route path={'/'}>
|
||||||
<EnsureAuthenticated>
|
<PrivateRoute component={DashboardPrivatePages} />
|
||||||
<EnsureUserEmailVerified>
|
|
||||||
<DashboardPrivatePages />
|
|
||||||
</EnsureUserEmailVerified>
|
|
||||||
</EnsureAuthenticated>
|
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { useEffect } from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
useAuthenticatedAccount,
|
useAuthenticatedAccount,
|
||||||
useCurrentOrganization,
|
useCurrentOrganization,
|
||||||
@@ -116,14 +116,6 @@ export function useApplicationBoot() {
|
|||||||
isBooted.current = true;
|
isBooted.current = true;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// Reset the loading states once the hook unmount.
|
|
||||||
useEffect(
|
|
||||||
() => () => {
|
|
||||||
isAuthUserLoading && !isBooted.current && stopLoading();
|
|
||||||
isOrgLoading && !isBooted.current && stopLoading();
|
|
||||||
},
|
|
||||||
[isAuthUserLoading, isOrgLoading, stopLoading],
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isLoading: isOrgLoading || isAuthUserLoading,
|
isLoading: isOrgLoading || isAuthUserLoading,
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ import EstimateMailDialog from '@/containers/Sales/Estimates/EstimateMailDialog/
|
|||||||
import ReceiptMailDialog from '@/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialog';
|
import ReceiptMailDialog from '@/containers/Sales/Receipts/ReceiptMailDialog/ReceiptMailDialog';
|
||||||
import PaymentMailDialog from '@/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialog';
|
import PaymentMailDialog from '@/containers/Sales/PaymentReceives/PaymentMailDialog/PaymentMailDialog';
|
||||||
import { ConnectBankDialog } from '@/containers/CashFlow/ConnectBankDialog';
|
import { ConnectBankDialog } from '@/containers/CashFlow/ConnectBankDialog';
|
||||||
import { ExportDialog } from '@/containers/Dialogs/ExportDialog';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialogs container.
|
* Dialogs container.
|
||||||
@@ -149,8 +148,6 @@ export default function DialogsContainer() {
|
|||||||
<ReceiptMailDialog dialogName={DialogsName.ReceiptMail} />
|
<ReceiptMailDialog dialogName={DialogsName.ReceiptMail} />
|
||||||
<PaymentMailDialog dialogName={DialogsName.PaymentMail} />
|
<PaymentMailDialog dialogName={DialogsName.PaymentMail} />
|
||||||
<ConnectBankDialog dialogName={DialogsName.ConnectBankCreditCard} />
|
<ConnectBankDialog dialogName={DialogsName.ConnectBankCreditCard} />
|
||||||
|
|
||||||
<ExportDialog dialogName={DialogsName.Export} />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import React from 'react';
|
|
||||||
import { Redirect } from 'react-router-dom';
|
|
||||||
import { useIsAuthenticated } from '@/hooks/state';
|
|
||||||
|
|
||||||
interface EnsureAuthNotAuthenticatedProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
redirectTo?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function EnsureAuthNotAuthenticated({
|
|
||||||
children,
|
|
||||||
redirectTo = '/',
|
|
||||||
}: EnsureAuthNotAuthenticatedProps) {
|
|
||||||
const isAuthenticated = useIsAuthenticated();
|
|
||||||
|
|
||||||
return !isAuthenticated ? (
|
|
||||||
<>{children}</>
|
|
||||||
) : (
|
|
||||||
<Redirect to={{ pathname: redirectTo }} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import React from 'react';
|
|
||||||
import { Redirect } from 'react-router-dom';
|
|
||||||
import { useIsAuthenticated } from '@/hooks/state';
|
|
||||||
|
|
||||||
interface EnsureAuthenticatedProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
redirectTo?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function EnsureAuthenticated({
|
|
||||||
children,
|
|
||||||
redirectTo = '/auth/login',
|
|
||||||
}: EnsureAuthenticatedProps) {
|
|
||||||
const isAuthenticated = useIsAuthenticated();
|
|
||||||
|
|
||||||
return isAuthenticated ? (
|
|
||||||
<>{children}</>
|
|
||||||
) : (
|
|
||||||
<Redirect to={{ pathname: redirectTo }} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Redirect } from 'react-router-dom';
|
|
||||||
import { useAuthUserVerified } from '@/hooks/state';
|
|
||||||
|
|
||||||
interface EnsureUserEmailNotVerifiedProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
redirectTo?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Higher Order Component to ensure that the user's email is not verified.
|
|
||||||
* If is verified, redirects to the inner setup page.
|
|
||||||
*/
|
|
||||||
export function EnsureUserEmailNotVerified({
|
|
||||||
children,
|
|
||||||
redirectTo = '/',
|
|
||||||
}: EnsureUserEmailNotVerifiedProps) {
|
|
||||||
const isAuthVerified = useAuthUserVerified();
|
|
||||||
|
|
||||||
if (isAuthVerified) {
|
|
||||||
return <Redirect to={{ pathname: redirectTo }} />;
|
|
||||||
}
|
|
||||||
return <>{children}</>;
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Redirect } from 'react-router-dom';
|
|
||||||
import { useAuthUserVerified } from '@/hooks/state';
|
|
||||||
|
|
||||||
interface EnsureUserEmailVerifiedProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
redirectTo?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Higher Order Component to ensure that the user's email is verified.
|
|
||||||
* If not verified, redirects to the email verification page.
|
|
||||||
*/
|
|
||||||
export function EnsureUserEmailVerified({
|
|
||||||
children,
|
|
||||||
redirectTo = '/auth/register/verify',
|
|
||||||
}: EnsureUserEmailVerifiedProps) {
|
|
||||||
const isAuthVerified = useAuthUserVerified();
|
|
||||||
|
|
||||||
if (!isAuthVerified) {
|
|
||||||
return <Redirect to={{ pathname: redirectTo }} />;
|
|
||||||
}
|
|
||||||
return <>{children}</>;
|
|
||||||
}
|
|
||||||
19
packages/webapp/src/components/Guards/PrivateRoute.tsx
Normal file
19
packages/webapp/src/components/Guards/PrivateRoute.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import React from 'react';
|
||||||
|
import BodyClassName from 'react-body-classname';
|
||||||
|
import { Redirect } from 'react-router-dom';
|
||||||
|
import { useIsAuthenticated } from '@/hooks/state';
|
||||||
|
|
||||||
|
export default function PrivateRoute({ component: Component, ...rest }) {
|
||||||
|
const isAuthenticated = useIsAuthenticated();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BodyClassName className={''}>
|
||||||
|
{isAuthenticated ? (
|
||||||
|
<Component />
|
||||||
|
) : (
|
||||||
|
<Redirect to={{ pathname: '/auth/login' }} />
|
||||||
|
)}
|
||||||
|
</BodyClassName>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -73,6 +73,5 @@ export enum DialogsName {
|
|||||||
CustomerTransactionsPdfPreview = 'CustomerTransactionsPdfPreview',
|
CustomerTransactionsPdfPreview = 'CustomerTransactionsPdfPreview',
|
||||||
VendorTransactionsPdfPreview = 'VendorTransactionsPdfPreview',
|
VendorTransactionsPdfPreview = 'VendorTransactionsPdfPreview',
|
||||||
GeneralLedgerPdfPreview = 'GeneralLedgerPdfPreview',
|
GeneralLedgerPdfPreview = 'GeneralLedgerPdfPreview',
|
||||||
SalesTaxLiabilitySummaryPdfPreview = 'SalesTaxLiabilitySummaryPdfPreview',
|
SalesTaxLiabilitySummaryPdfPreview = 'SalesTaxLiabilitySummaryPdfPreview'
|
||||||
Export = 'Export',
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
Can,
|
Can,
|
||||||
If,
|
If,
|
||||||
DashboardActionViewsList,
|
DashboardActionViewsList,
|
||||||
DashboardActionsBar,
|
DashboardActionsBar
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { useRefreshJournals } from '@/hooks/query/manualJournals';
|
import { useRefreshJournals } from '@/hooks/query/manualJournals';
|
||||||
import { useManualJournalsContext } from './ManualJournalsListProvider';
|
import { useManualJournalsContext } from './ManualJournalsListProvider';
|
||||||
@@ -31,7 +31,6 @@ import withSettingsActions from '@/containers/Settings/withSettingsActions';
|
|||||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||||
|
|
||||||
import { compose } from '@/utils';
|
import { compose } from '@/utils';
|
||||||
import { DialogsName } from '@/constants/dialogs';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manual journal actions bar.
|
* Manual journal actions bar.
|
||||||
@@ -48,9 +47,6 @@ function ManualJournalActionsBar({
|
|||||||
|
|
||||||
// #withSettingsActions
|
// #withSettingsActions
|
||||||
addSetting,
|
addSetting,
|
||||||
|
|
||||||
// #withDialogActions
|
|
||||||
openDialog
|
|
||||||
}) {
|
}) {
|
||||||
// History context.
|
// History context.
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
@@ -79,18 +75,13 @@ function ManualJournalActionsBar({
|
|||||||
// Handle import button click.
|
// Handle import button click.
|
||||||
const handleImportBtnClick = () => {
|
const handleImportBtnClick = () => {
|
||||||
history.push('/manual-journals/import');
|
history.push('/manual-journals/import');
|
||||||
};
|
}
|
||||||
|
|
||||||
// Handle table row size change.
|
// Handle table row size change.
|
||||||
const handleTableRowSizeChange = (size) => {
|
const handleTableRowSizeChange = (size) => {
|
||||||
addSetting('manualJournals', 'tableSize', size);
|
addSetting('manualJournals', 'tableSize', size);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle the export button click.
|
|
||||||
const handleExportBtnClick = () => {
|
|
||||||
openDialog(DialogsName.Export, { resource: 'manual_journal' });
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardActionsBar>
|
<DashboardActionsBar>
|
||||||
<NavbarGroup>
|
<NavbarGroup>
|
||||||
@@ -149,7 +140,6 @@ function ManualJournalActionsBar({
|
|||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon="file-export-16" iconSize={16} />}
|
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||||
text={<T id={'export'} />}
|
text={<T id={'export'} />}
|
||||||
onClick={handleExportBtnClick}
|
|
||||||
/>
|
/>
|
||||||
<NavbarDivider />
|
<NavbarDivider />
|
||||||
<DashboardRowsHeightButton
|
<DashboardRowsHeightButton
|
||||||
|
|||||||
@@ -118,10 +118,6 @@ function AccountsActionsBar({
|
|||||||
const handleImportBtnClick = () => {
|
const handleImportBtnClick = () => {
|
||||||
history.push('/accounts/import');
|
history.push('/accounts/import');
|
||||||
};
|
};
|
||||||
// Handle the export button click.
|
|
||||||
const handleExportBtnClick = () => {
|
|
||||||
openDialog(DialogsName.Export, { resource: 'account' });
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardActionsBar>
|
<DashboardActionsBar>
|
||||||
@@ -188,15 +184,14 @@ function AccountsActionsBar({
|
|||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon="file-import-16" iconSize={16} />}
|
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||||
text={<T id={'import'} />}
|
text={<T id={'export'} />}
|
||||||
onClick={handleImportBtnClick}
|
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon="file-export-16" iconSize={16} />}
|
icon={<Icon icon="file-import-16" iconSize={16} />}
|
||||||
text={<T id={'export'} />}
|
text={<T id={'import'} />}
|
||||||
onClick={handleExportBtnClick}
|
onClick={handleImportBtnClick}
|
||||||
/>
|
/>
|
||||||
<NavbarDivider />
|
<NavbarDivider />
|
||||||
<DashboardRowsHeightButton
|
<DashboardRowsHeightButton
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { Icon, FormattedMessage as T } from '@/components';
|
|
||||||
|
|
||||||
interface AuthContainerProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AuthContainer({ children }: AuthContainerProps) {
|
|
||||||
return (
|
|
||||||
<AuthPage>
|
|
||||||
<AuthInsider>
|
|
||||||
<AuthLogo>
|
|
||||||
<Icon icon="bigcapital" height={37} width={214} />
|
|
||||||
</AuthLogo>
|
|
||||||
|
|
||||||
{children}
|
|
||||||
</AuthInsider>
|
|
||||||
</AuthPage>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const AuthPage = styled.div``;
|
|
||||||
const AuthInsider = styled.div`
|
|
||||||
width: 384px;
|
|
||||||
margin: 0 auto;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
padding-top: 80px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const AuthLogo = styled.div`
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
`;
|
|
||||||
@@ -1,16 +1,24 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { Route, Switch, useLocation } from 'react-router-dom';
|
import React from 'react';
|
||||||
|
import { Redirect, Route, Switch, useLocation } from 'react-router-dom';
|
||||||
import BodyClassName from 'react-body-classname';
|
import BodyClassName from 'react-body-classname';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { TransitionGroup, CSSTransition } from 'react-transition-group';
|
import { TransitionGroup, CSSTransition } from 'react-transition-group';
|
||||||
|
|
||||||
import authenticationRoutes from '@/routes/authentication';
|
import authenticationRoutes from '@/routes/authentication';
|
||||||
import { Icon, FormattedMessage as T } from '@/components';
|
import { Icon, FormattedMessage as T } from '@/components';
|
||||||
|
import { useIsAuthenticated } from '@/hooks/state';
|
||||||
import { AuthMetaBootProvider } from './AuthMetaBoot';
|
import { AuthMetaBootProvider } from './AuthMetaBoot';
|
||||||
|
|
||||||
import '@/style/pages/Authentication/Auth.scss';
|
import '@/style/pages/Authentication/Auth.scss';
|
||||||
|
|
||||||
export function Authentication() {
|
export function Authentication() {
|
||||||
|
const to = { pathname: '/' };
|
||||||
|
const isAuthenticated = useIsAuthenticated();
|
||||||
|
|
||||||
|
if (isAuthenticated) {
|
||||||
|
return <Redirect to={to} />;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<BodyClassName className={'authentication'}>
|
<BodyClassName className={'authentication'}>
|
||||||
<AuthPage>
|
<AuthPage>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user