Compare commits

..

2 Commits

Author SHA1 Message Date
Ahmed Bouhuolia
74b6e41cd4 feat: Add swagger decorators 2024-09-07 15:46:49 +02:00
Ahmed Bouhuolia
d239003047 feat: Document API endpoints using Swagger 2024-09-07 15:17:10 +02:00
775 changed files with 5534 additions and 48300 deletions

View File

@@ -159,15 +159,6 @@
"contributions": [ "contributions": [
"code" "code"
] ]
},
{
"login": "nklmantey",
"name": "Mantey",
"avatar_url": "https://avatars.githubusercontent.com/u/90279429?v=4",
"profile": "https://nklmantey.com/",
"contributions": [
"bug"
]
} }
], ],
"contributorsPerLine": 7, "contributorsPerLine": 7,

View File

@@ -92,11 +92,4 @@ S3_BUCKET=
# PostHog # PostHog
POSTHOG_API_KEY= POSTHOG_API_KEY=
POSTHOG_HOST= POSTHOG_HOST=
# Stripe Payment
STRIPE_PAYMENT_SECRET_KEY=
STRIPE_PAYMENT_PUBLISHABLE_KEY=
STRIPE_PAYMENT_CLIENT_ID=
STRIPE_PAYMENT_WEBHOOKS_SECRET=
STRIPE_PAYMENT_REDIRECT_URL=

View File

@@ -2,191 +2,7 @@
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.22.0] ## [0.19.4] - 18-08-2024
* feat: estimate, receipt, credit note mail preview by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/757
* feat: Add discount to transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/758
* fix: update financial forms to use new formatted amount utilities and… by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/760
* fix: total lines style by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/761
* fix: discount & adjustment sale transactions bugs by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/762
* fix: discount transactions GL entries by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/763
# [0.21.2]
* hotbug: upload attachments by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/755
# [0.21.1]
* fix: download invoice document on payment page by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/750
* fix: attach branding template attrs to payment page by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/751
* fix: make manual entries adjust decimal credit/debit amounts by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/754
* feat: allow quantity of entries accept decimal value by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/753
# [0.21.0]
* fix: Credit and debit totals not balancing when decimal values are used by @nklmantey in https://github.com/bigcapitalhq/bigcapital/pull/722
* docs: add nklmantey as a contributor for bug by @allcontributors in https://github.com/bigcapitalhq/bigcapital/pull/725
* feat: track more services events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/721
* feat: Invoice mail receipt preview by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/723
* fix: change the send mail button on invoice drawer by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/730
* refactor: notification mail services by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/731
* fix: attach payment link in sending invoice mail receipt by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/732
* fix: send invoice drawer layout by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/733
* fix: hook up cc and bcc fields to mail sender by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/734
* fix: company logo does not show up in mail receipt preview by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/736
* fix: change default invoice mail message by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/737
* fix: typing invoice send mail fields by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/738
* fix: clean up ivnoice mail receipt preview component by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/739
* feat: add shared package to pdf templates to render in the server and… by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/735
* feat: getting invoice preview on send mail view by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/740
* fix: style SSR invoice paper template by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/741
* fix: send invoice receipt addresses by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/742
* fix: due invoice server invoice by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/744
* fix: `BIG-265` forgot password text by @ibutiti in https://github.com/bigcapitalhq/bigcapital/pull/745
* Crims on sv translation by @Crims-on in https://github.com/bigcapitalhq/bigcapital/pull/671
* feat: Added Spanish language to the App 🇪🇸 by @angelosorno in https://github.com/bigcapitalhq/bigcapital/pull/530
* fix: mail services by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/746
* fix: company logo of the template by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/747
* fix: monorepo dependencies scope by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/748
# [0.20.6]
* fix: Import category column of item resource by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/710
* fix: Parse the uppercase values in importing by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/711
* chore: Move i18nApply localization to the account transformer by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/713
* fix: Sync Plaid credit card account type by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/714
* fix: Sync account normal of cashflow GL entries by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/715
* feat: Add quantity column to pdf templates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/716
* feat: Pre-line invoice statements by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/717
* feat: Invoice number in downloaded pdf document by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/718
* feat: Track events of pdf documents views by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/719
* fix: Customer note does not appear in pdf document by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/720
# [0.20.5]
* fix: Disable tabs of the pdf customization if the first field not filed up by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/701
* fix: Invoice form layout by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/705
* refactor: Invoice, estimate, receipt, credit note and payment received date input fields by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/707
* feat: Add customize templates button to edit forms by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/708
* feat: Track account, invoice and item viewed events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/709
# [0.20.4]
* fix: Delete company logo from the PDF template by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/699
* fix: Set max width/height to company logo of pdf templates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/700
# [0.20.3]
* feat: Assign default PDF template automatically by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/687
* fix: pdf template addresses controlling by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/688
* fix: Remove empty lines from address formats by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/690
* fix: Pdf templates layout by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/691
* feat: Download invoice pdf of the payment link by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/689
* fix: Display country name by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/693
* feat: Add shared packages to Docker container by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/694
# [0.20.2]
* feat: Assign default PDF template automatically by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/687
* fix: pdf template addresses controlling by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/688
* fix: Remove empty lines from address formats by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/690
* fix: Pdf templates layout by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/691
* feat: Download invoice pdf of the payment link by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/689
* fix: Display country name by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/693
* feat: Add shared packages to Docker container by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/694
# [0.20.1]
* fix: Getting uploaded object uri by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/684
# [0.20.0]
* feat: Customize pdf templates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/667
* feat: Onboard accounts to Stripe Connect by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/668
* feat: Upload company logo to invoice templates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/670
* fix: Invoice pdf customize by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/672
* fix: Invoice customize bugs by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/673
* feat: Clean up payment links endpoints by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/674
* feat: Hook up company logo to server-side pdf templates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/675
* feat: Company branding preferences by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/677
* feat: Pdf templates customer/company addresses by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/678
* fix: Listen to Stripe session completed event by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/679
* feat: Track pdf templates Posthog events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/680
* fix: Branding customize content by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/681
* feat: Listen to Stripe integration events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/682
* feat: Hook up customer/company address to invoice preview of payment page by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/683
# [0.19.17]
* fix: Un-categorize bank transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/663
# [0.19.16]
* feat: Tracking more Posthog events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/653
* fix: Expense cannot accept credit card as payment account by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/654
* fix: Suspense the lazy loaded components in banking pages by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/657
* feat: Add help dropdown menu by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/656
* feat: Bank pages layout breaking by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/658
* feat: Datatable UI improvements by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/655
* fix: Array cast of recognize function rule ids by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/660
* fix: Payment made filling the form full amount field by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/661
* feat: Tabular number of all money columns by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/659
* refactor: The expense G/L writer by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/662
## [0.19.15] -
* fix: Bank transactions infinity scrolling by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/648
* feat: Integrate multiple branches and warehouses to resource importing by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/645
* fix: Integrate multiple branches with expense resource by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/649
* feat: Cover more tracking events. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/650
* feat: Track banking service events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/651
## [0.19.14]
* fix: Import bugs by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/643
* fix: Set default index to transaction entries by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/644
* feat(server): Events tracking using Posthog by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/646
## [0.19.13]
* fix: Subscription middleware by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/624
* fix: Getting the sheet columns in import sheet by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/641
## [0.19.12]
* fix: Typo one-click demo page by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/640
## [0.19.11]
* fix: Avoid running the cost job in import preview by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/635
* fix: Debounce scheduling calculating items cost by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/634
* fix: Expand the resources export page size limitation by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/636
* feat: Optimize loading perf. by splitting big chunks and lazy loading them by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/632
* fix: Use standard ISO 8601 format for exported data by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/638
* fix: Add customer type to customers resource by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/639
## [0.19.10]
* fix: Add subscription plans offer text by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/629
## [0.19.9]
* fix: Make webapp package env variables dynamic by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/628
## [v0.19.8]
* fix: Cannot import items income and cost accounts by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/617
* fix: Some bank account details hidden by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/618
* feat(ee): One-click demo account by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/616
* feat: change banking service language by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/619
* feat(banking): Filter uncategorized bank transactions by date by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/590
* Fix: Syntax error caused error by @wolone in https://github.com/bigcapitalhq/bigcapital/pull/622
* fix: Listen to payment webhooks by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/623
* fix: Add prefix J-00001 to manual journals increments by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/625
* fix: Disable sms service until Twilo integration by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/626
* fix: Style tweaks in onboarding page by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/627
## [0.19.5] - 18-08-2024
* fix: Allow multi-lines to statements transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/594 * fix: Allow multi-lines to statements transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/594
* feat: Add amount comparators to amount bank rule field by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/595 * feat: Add amount comparators to amount bank rule field by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/595
@@ -207,15 +23,6 @@ All notable changes to Bigcapital server-side will be in this file.
* fix: Delete bank account with uncategorized transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/614 * fix: Delete bank account with uncategorized transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/614
* feat: activate/inactivate account from drawer details by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/615 * feat: activate/inactivate account from drawer details by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/615
## [v0.19.4]
* feat: Import and export tax rates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/591
* feat: Un-categorize bank transactions in bulk by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/587
* feat: Pending bank transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/589
* fix: Update `dev` Script in `package.json` to Use `cross-env` by @Champetaman in https://github.com/bigcapitalhq/bigcapital/pull/588
* fix: Should not load branches on reconcile matching form if the branches not enabled by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/592
* fix: Rounding the total amount the pending and matched transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/593
## [v0.18.0] - 10-08-2024 ## [v0.18.0] - 10-08-2024
* feat: Bank rules for automated categorization by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/511 * feat: Bank rules for automated categorization by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/511

View File

@@ -12,9 +12,6 @@
<a href="https://github.com/bigcapitalhq/bigcapital/commits/develop"> <a href="https://github.com/bigcapitalhq/bigcapital/commits/develop">
<img src="https://img.shields.io/github/commit-activity/m/bigcapitalhq/bigcapital/develop" /> <img src="https://img.shields.io/github/commit-activity/m/bigcapitalhq/bigcapital/develop" />
</a> </a>
<a href="https://hub.docker.com/u/bigcapitalhq">
<img src="https://img.shields.io/docker/pulls/bigcapitalhq/webapp" />
</a>
<a href="https://discord.com/invite/c8nPBJafeb"> <a href="https://discord.com/invite/c8nPBJafeb">
<img src="https://img.shields.io/discord/1066514716752625725?label=Discord" alt="" /> <img src="https://img.shields.io/discord/1066514716752625725?label=Discord" alt="" />
</a> </a>
@@ -133,7 +130,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/oleynikd"><img src="https://avatars.githubusercontent.com/u/3976868?v=4?s=100" width="100px;" alt="Denis"/><br /><sub><b>Denis</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Aoleynikd" title="Bug reports">🐛</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/oleynikd"><img src="https://avatars.githubusercontent.com/u/3976868?v=4?s=100" width="100px;" alt="Denis"/><br /><sub><b>Denis</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Aoleynikd" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://myself.vercel.app/"><img src="https://avatars.githubusercontent.com/u/42431274?v=4?s=100" width="100px;" alt="Sachin Mittal"/><br /><sub><b>Sachin Mittal</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Amittalsam98" title="Bug reports">🐛</a></td> <td align="center" valign="top" width="14.28%"><a href="https://myself.vercel.app/"><img src="https://avatars.githubusercontent.com/u/42431274?v=4?s=100" width="100px;" alt="Sachin Mittal"/><br /><sub><b>Sachin Mittal</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Amittalsam98" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.camilooviedo.com/"><img src="https://avatars.githubusercontent.com/u/64604272?v=4?s=100" width="100px;" alt="Camilo Oviedo"/><br /><sub><b>Camilo Oviedo</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=Champetaman" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://www.camilooviedo.com/"><img src="https://avatars.githubusercontent.com/u/64604272?v=4?s=100" width="100px;" alt="Camilo Oviedo"/><br /><sub><b>Camilo Oviedo</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=Champetaman" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://nklmantey.com/"><img src="https://avatars.githubusercontent.com/u/90279429?v=4?s=100" width="100px;" alt="Mantey"/><br /><sub><b>Mantey</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Anklmantey" title="Bug reports">🐛</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -3,7 +3,6 @@
"version": "independent", "version": "independent",
"npmClient": "pnpm", "npmClient": "pnpm",
"packages": [ "packages": [
"packages/*", "packages/*"
"shared/*"
] ]
} }

View File

@@ -4,11 +4,11 @@
"scripts": { "scripts": {
"dev": "lerna run dev", "dev": "lerna run dev",
"build": "lerna run build", "build": "lerna run build",
"dev:webapp": "lerna run dev --scope \"@bigcapital/webapp\" --scope \"@bigcapital/utils\" --scope \"@bigcapital/pdf-templates\"", "dev:webapp": "lerna run dev --scope \"@bigcapital/webapp\"",
"build:webapp": "lerna run build --scope \"@bigcapital/webapp\" --scope \"@bigcapital/utils\" --scope \"@bigcapital/pdf-templates\"", "build:webapp": "lerna run build --scope \"@bigcapital/webapp\"",
"dev:server": "lerna run dev --scope \"@bigcapital/server\" --scope \"@bigcapital/utils\" --scope \"@bigcapital/pdf-templates\" --scope \"@bigcapital/email-components\"", "dev:server": "lerna run dev --scope \"@bigcapital/server\"",
"build:server": "lerna run build --scope \"@bigcapital/server\" --scope \"@bigcapital/utils\" --scope \"@bigcapital/pdf-templates\" --scope \"@bigcapital/email-components\"", "build:server": "lerna run build --scope \"@bigcapital/server\"",
"serve:server": "lerna run serve --scope \"@bigcapital/server\" --scope \"@bigcapital/utils\"", "serve:server": "lerna run serve --scope \"@bigcapital/server\"",
"test:e2e": "playwright test", "test:e2e": "playwright test",
"prepare": "husky install" "prepare": "husky install"
}, },
@@ -29,8 +29,5 @@
"hooks": { "hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS" "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
} }
},
"dependencies": {
"tsup": "^8.3.0"
} }
} }

View File

@@ -4,5 +4,5 @@ stdout.log
/dist /dist
/build /build
/public/imports /public/imports
dist
newrelic_agent.log dist

View File

@@ -90,7 +90,11 @@ RUN chown node:node /
RUN npm install -g pnpm RUN npm install -g pnpm
# Copy application dependency manifests to the container image. # Copy application dependency manifests to the container image.
COPY --chown=node:node ./ ./ COPY ./package*.json ./
COPY ./pnpm-lock.yaml ./pnpm-lock.yaml
COPY ./lerna.json ./lerna.json
COPY ./pnpm-workspace.yaml ./pnpm-workspace.yaml
COPY ./packages/server/package*.json ./packages/server/
# Install application dependencies # Install application dependencies
RUN apk update RUN apk update
@@ -105,6 +109,6 @@ RUN pnpm install
COPY --chown=node:node ./packages/server ./packages/server COPY --chown=node:node ./packages/server ./packages/server
# # Creates a "dist" folder with the production build # # Creates a "dist" folder with the production build
RUN pnpm run build:server --skip-nx-cache RUN npm run build:server --skip-nx-cache
CMD [ "node", "./packages/server/build/index.js" ] CMD [ "node", "./packages/server/build/index.js" ]

View File

@@ -20,9 +20,6 @@
"bigcapital": "./bin/bigcapital.js" "bigcapital": "./bin/bigcapital.js"
}, },
"dependencies": { "dependencies": {
"@bigcapital/utils": "*",
"@bigcapital/email-components": "*",
"@bigcapital/pdf-templates": "*",
"@aws-sdk/client-s3": "^3.576.0", "@aws-sdk/client-s3": "^3.576.0",
"@aws-sdk/s3-request-presigner": "^3.583.0", "@aws-sdk/s3-request-presigner": "^3.583.0",
"@casl/ability": "^5.4.3", "@casl/ability": "^5.4.3",
@@ -112,13 +109,12 @@
"rtl-detect": "^1.0.4", "rtl-detect": "^1.0.4",
"socket.io": "^4.7.4", "socket.io": "^4.7.4",
"source-map-loader": "^4.0.1", "source-map-loader": "^4.0.1",
"stripe": "^16.10.0", "swagger-ui-express": "^5.0.1",
"tmp-promise": "^3.0.3", "tmp-promise": "^3.0.3",
"ts-transformer-keys": "^0.4.2", "ts-transformer-keys": "^0.4.2",
"tsyringe": "^4.3.0", "tsyringe": "^4.3.0",
"typedi": "^0.8.0", "typedi": "^0.8.0",
"uniqid": "^5.2.0", "uniqid": "^5.2.0",
"uuid": "^10.0.0",
"winston": "^3.2.1", "winston": "^3.2.1",
"xlsx": "^0.18.5", "xlsx": "^0.18.5",
"yup": "^0.28.1" "yup": "^0.28.1"

View File

@@ -1,40 +0,0 @@
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap');
*,
*::before,
*::after {
box-sizing: border-box;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
body{
margin: 0;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #000;
background-color: #fff;
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: transparent;
}
body, h1, h2, h3, h4, h5, h6{
font-family: "Noto Sans", sans-serif;
font-optical-sizing: auto;
font-style: normal;
}

View File

@@ -1 +1,35 @@
@import "./normalize.scss"; @import "./normalize.scss";
*,
*::before,
*::after {
box-sizing: border-box;
}
th {
text-align: inherit; // 2
text-align: -webkit-match-parent; // 3
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
body{
margin: 0;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
background-color: #fff;
direction: ltr;
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: transparent;
}

View File

@@ -1,379 +0,0 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15;
/* 1 */
-webkit-text-size-adjust: 100%;
/* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box;
/* 1 */
height: 0;
/* 1 */
overflow: visible;
/* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace;
/* 1 */
font-size: 1em;
/* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none;
/* 1 */
text-decoration: underline;
/* 2 */
text-decoration: underline dotted;
/* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace;
/* 1 */
font-size: 1em;
/* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit;
/* 1 */
font-size: 100%;
/* 1 */
line-height: 1.15;
/* 1 */
margin: 0;
/* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input {
/* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select {
/* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box;
/* 1 */
color: inherit;
/* 2 */
display: table;
/* 1 */
max-width: 100%;
/* 1 */
padding: 0;
/* 3 */
white-space: normal;
/* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box;
/* 1 */
padding: 0;
/* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield;
/* 1 */
outline-offset: -2px;
/* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button;
/* 1 */
font: inherit;
/* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}

View File

@@ -1,9 +1,6 @@
html(lang=locale) html(lang=locale)
head head
title My Site - #{title} title My Site - #{title}
style
include ../scss/normalize.css
include ../scss/base.css
block head block head
body body
div.paper-template div.paper-template

View File

@@ -1,227 +1,81 @@
extends ../PaperTemplateLayout.pug extends ../PaperTemplateLayout.pug
block head block head
- var prefix = 'bc' style
style. if (isRtl)
.#{prefix}-root { include ../../css/modules/credit-rtl.css
color: #111; else
padding: 24px 30px; include ../../css/modules/credit.css
font-size: 12px;
position: relative;
box-shadow: inset 0 4px 0px 0 var(--invoice-primary-color);
}
.#{prefix}-header{
box-sizing: border-box;
display: flex;
flex-flow: wrap;
flex: 0 0 auto;
-webkit-box-align: start;
align-items: start;
-webkit-box-pack: start;
justify-content: flex-start;
gap: 10px;
}
.#{prefix}-header-details{
flex: 1;
display: flex;
flex-direction: column;
align-items: stretch;
gap: 20px;
flex: 1 1 0%;
}
.#{prefix}-big-title {
font-size: 30px;
margin: 0;
line-height: 1;
font-weight: 500;
color: #333;
}
.#{prefix}-logo-wrap img {
width: 100%;
height: 100%;
max-width: 260px;
max-height: 100px;
}
.#{prefix}-terms-list {
display: flex;
flex-direction: column;
gap: 4px;
margin-bottom: 24px;
}
.#{prefix}-terms-item {
display: flex;
flex-direction: row;
gap: 12px;
}
.#{prefix}-terms-item__label {
min-width: 120px;
color: #333;
}
.#{prefix}-terms-item__value {
/* Styles for the term value */
}
.#{prefix}-address-section{
box-sizing: border-box;
display: flex;
flex-flow: wrap;
-webkit-box-align: flex-start;
align-items: flex-start;
-webkit-box-pack: start;
justify-content: flex-start;
gap: 10px;
margin-bottom: 24px;
}
.#{prefix}-address-section > * {
flex: 1 1;
}
.#{prefix}-table {
width: 100%;
border-collapse: collapse;
text-align: left;
font-size: inherit;
}
.#{prefix}-table__header {
font-weight: 400;
border-bottom: 1px solid #000;
padding: 2px 10px;
color: #333;
}
.#{prefix}-table__header:first-of-type{
padding-left: 0;
}
.#{prefix}-table__header:last-of-type{
padding-right: 0;
}
.#{prefix}-table__header--right {
text-align: right;
}
.#{prefix}-table__cell {
border-bottom: 1px solid #F6F6F6;
padding: 12px 10px;
}
.#{prefix}-table__cell:first-of-type{
padding-left: 0;
}
.#{prefix}-table__cell:last-of-type {
padding-right: 0;
}
.#{prefix}-table__cell--right {
text-align: right;
}
.#{prefix}-table__cell--item .item {
display: flex;
flex-direction: column;
gap: 2px;
}
.#{prefix}-table__cell--item .item .item__description{
color: #5f6b7c;
}
.#{prefix}-totals {
display: flex;
flex-direction: column;
margin-left: auto;
width: 300px;
margin-bottom: 24px;
}
.#{prefix}-totals__item {
display: flex;
padding: 4px 0;
}
.#{prefix}-totals__item--border-gray {
border-bottom: 1px solid #DADADA;
}
.#{prefix}-totals__item--border-dark {
border-bottom: 1px solid #000;
}
.#{prefix}-totals__item--font-weight-bold {
font-weight: bold;
}
.#{prefix}-totals__item-label {
min-width: 160px;
}
.#{prefix}-totals__item-amount {
flex: 1 1 auto;
text-align: right;
}
.#{prefix}-statement {
margin-bottom: 20px;
}
.#{prefix}-statement__label {
color: #666;
}
.#{prefix}-statement__value {
white-space: pre-line;
}
block content block content
div(class=`${prefix}-root`) div.credit
div.credit__header
//- Header (includes big title, details and logo) div.paper
div(class=`${prefix}-header`) h1.title #{__('credit.paper.credit_note')}
//- Header details (includes big title and details) if creditNote.creditNoteNumber
div(class=`${prefix}-header-details`) span.creditNoteNumber #{creditNote.creditNoteNumber}
div(class=`${prefix}-big-title`) Credit Note
div(class=`${prefix}-terms-list`) div.organization
if showCreditNoteNumber h3.title #{organizationName}
div(class=`${prefix}-terms-item`) if organizationEmail
div(class=`${prefix}-terms-item__label`) #{creditNoteNumberLabel}: span.email #{organizationEmail}
div(class=`${prefix}-terms-item__value`) #{creditNoteNumebr}
if showCreditNoteDate div.credit__full-amount
div(class=`${prefix}-terms-item`) div.label #{__('credit.paper.amount')}
div(class=`${prefix}-terms-item__label`) #{creditNoteDateLabel}: div.amount #{creditNote.formattedAmount}
div(class=`${prefix}-terms-item__value`) #{creditNoteDate}
if showCompanyLogo && companyLogoUri div.credit__meta
div(class=`${prefix}-logo-wrap`) div.credit__meta-item.credit__meta-item--amount
img(src=companyLogoUri alt=`Company Logo`) span.label #{__('credit.paper.remaining')}
span.value #{creditNote.formattedCreditsRemaining}
div(class=`${prefix}-address-section`) div.credit__meta-item.credit__meta-item--billed-to
if showCompanyAddress span.label #{__("credit.paper.billed_to")}
div(class=`${prefix}-address-from`) span.value #{creditNote.customer.displayName}
div !{companyAddress}
if showCustomerAddress div.credit__meta-item.credit__meta-item--credit-date
div(class=`${prefix}-address-to`) span.label #{__("credit.paper.credit_date")}
strong #{billedToLabel} span.value #{creditNote.formattedCreditNoteDate}
div !{customerAddress}
table(class=`${prefix}-table`) div.credit__table
thead table
tr thead
th(class=`${prefix}-table__header ${prefix}-table__header--item`) #{'Item'} tr
th(class=`${prefix}-table__header ${prefix}-table__header--quantity ${prefix}-table__header--right`) #{'Quantity'} th.item #{__("item_entry.paper.item_name")}
th(class=`${prefix}-table__header ${prefix}-table__header--rate ${prefix}-table__header--right`) #{'Rate'} th.rate #{__("item_entry.paper.rate")}
th(class=`${prefix}-table__header ${prefix}-table__header--total ${prefix}-table__header--right`) #{'Total'} th.quantity #{__("item_entry.paper.quantity")}
th.total #{__("item_entry.paper.total")}
tbody tbody
each line in lines each entry in creditNote.entries
tr(class=`${prefix}-table__row`) tr
td(class=`${prefix}-table__cell ${prefix}-table__cell--item ${prefix}-table__cell--item`) td.item
div.item div.title=entry.item.name
div.item__label #{line.item} span.description=entry.description
div.item__description #{line.description} td.rate=entry.rate
td(class=`${prefix}-table__cell ${prefix}-table__cell--quantity ${prefix}-table__cell--right`) #{line.quantity} td.quantity=entry.quantity
td(class=`${prefix}-table__cell ${prefix}-table__cell--rate ${prefix}-table__cell--right`) #{line.rate} td.total=entry.amount
td(class=`${prefix}-table__cell ${prefix}-table__cell--total ${prefix}-table__cell--right`) #{line.total}
div(class=`${prefix}-totals`) div.credit__table-after
if showSubtotal div.credit__table-total
div(class=`${prefix}-totals__item ${prefix}-totals__item--border-gray`) table
div(class=`${prefix}-totals__item-label`) #{subtotallabel} tbody
div(class=`${prefix}-totals__item-amount`) #{subtotal} tr.total
td #{__('credit.paper.total')}
td #{creditNote.formattedAmount}
tr.payment-amount
td #{__('credit.paper.credits_used')}
td #{creditNote.formattedCreditsUsed}
tr.blanace-due
td #{__('credit.paper.credits_remaining')}
td #{creditNote.formattedCreditsRemaining}
if showTotal div.credit__footer
div(class=`${prefix}-totals__item ${prefix}-totals__item--border-dark`) if creditNote.termsConditions
div(class=`${prefix}-totals__item-amount`) #{totalLabel}: div.credit__conditions
div(class=`${prefix}-totals__item-label`) #{total} h3 #{__("credit.paper.terms_conditions")}
p #{creditNote.termsConditions}
if showCustomerNote && customerNote if creditNote.note
div(class=`${prefix}-statement`) div.credit__notes
div(class=`${prefix}-statement__label`) #{customerNoteLabel}: h3 #{__("credit.paper.notes")}
div(class=`${prefix}-statement__value`) #{customerNote} p #{creditNote.note}
if showTermsConditions && termsConditions
div(class=`${prefix}-statement`)
div(class=`${prefix}-statement__label`) #{termsConditionsLabel}:
div(class=`${prefix}-statement__value`) #{termsConditions}

View File

@@ -0,0 +1,82 @@
extends ../PaperTemplateLayout.pug
block head
style
if (isRtl)
include ../../css/modules/estimate-rtl.css
else
include ../../css/modules/estimate.css
block content
div.estimate
div.estimate__header
div.paper
h1.title #{__("estimate.paper.estimate")}
span.email #{saleEstimate.estimateNumber}
div.organization
h3.title #{organizationName}
if organizationEmail
span.email #{organizationEmail}
div.estimate__estimate-amount
div.label #{__('estimate.paper.estimate_amount')}
div.amount #{saleEstimate.formattedAmount}
div.estimate__meta
if saleEstimate.estimateNumber
div.estimate__meta-item.estimate__meta-item--estimate-number
span.label #{__("estimate.paper.estimate_number")}
span.value #{saleEstimate.estimateNumber}
div.estimate__meta-item.estimate__meta-item--billed-to
span.label #{__("estimate.paper.billed_to")}
span.value #{saleEstimate.customer.displayName}
div.estimate__meta-item.estimate__meta-item--estimate-date
span.label #{__("estimate.paper.estimate_date")}
span.value #{saleEstimate.formattedEstimateDate}
div.estimate__meta-item.estimate__meta-item--due-date
span.label #{__("estimate.paper.expiration_date")}
span.value #{saleEstimate.formattedExpirationDate}
div.estimate__table
table
thead
tr
th.item #{__("item_entry.paper.item_name")}
th.rate #{__("item_entry.paper.rate")}
th.quantity #{__("item_entry.paper.quantity")}
th.total #{__("item_entry.paper.total")}
tbody
each entry in saleEstimate.entries
tr
td.item
div.title=entry.item.name
span.description=entry.description
td.rate=entry.rate
td.quantity=entry.quantity
td.total=entry.amount
div.estimate__table-after
div.estimate__table-total
table
tbody
tr.subtotal
td #{__('estimate.paper.subtotal')}
td #{saleEstimate.formattedAmount}
tr.total
td #{__('estimate.paper.total')}
td #{saleEstimate.formattedAmount}
div.estimate__footer
if saleEstimate.termsConditions
div.estimate__conditions
h3 #{__("estimate.paper.conditions_title")}
p #{saleEstimate.termsConditions}
if saleEstimate.note
div.estimate__notes
h3 #{__("estimate.paper.notes_title")}
p #{saleEstimate.note}

View File

@@ -0,0 +1,92 @@
extends ../PaperTemplateLayout.pug
block head
style
if (isRtl)
include ../../css/modules/invoice-rtl.css
else
include ../../css/modules/invoice.css
block content
div.invoice
div.invoice__header
div.paper
h1.title #{__("invoice.paper.invoice")}
if saleInvoice.invoiceNo
span.invoiceNo #{saleInvoice.invoiceNo}
div.organization
h3.title #{organizationName}
if organizationEmail
span.email #{organizationEmail}
div.invoice__due-amount
div.label #{__('invoice.paper.invoice_amount')}
div.amount #{saleInvoice.totalFormatted}
div.invoice__meta
div.invoice__meta-item.invoice__meta-item--amount
span.label #{__('invoice.paper.due_amount')}
span.value #{saleInvoice.dueAmountFormatted}
div.invoice__meta-item.invoice__meta-item--billed-to
span.label #{__("invoice.paper.billed_to")}
span.value #{saleInvoice.customer.displayName}
div.invoice__meta-item.invoice__meta-item--invoice-date
span.label #{__("invoice.paper.invoice_date")}
span.value #{saleInvoice.invoiceDateFormatted}
div.invoice__meta-item.invoice__meta-item--due-date
span.label #{__("invoice.paper.due_date")}
span.value #{saleInvoice.dueDateFormatted}
div.invoice__table
table
thead
tr
th.item #{__("item_entry.paper.item_name")}
th.rate #{__("item_entry.paper.rate")}
th.quantity #{__("item_entry.paper.quantity")}
th.total #{__("item_entry.paper.total")}
tbody
each entry in saleInvoice.entries
tr
td.item
div.title=entry.item.name
span.description=entry.description
td.rate=entry.rate
td.quantity=entry.quantity
td.total=entry.amount
div.invoice__table-after
div.invoice__table-total
table
tbody
tr.subtotal
td #{__('invoice.paper.subtotal')}
td #{saleInvoice.subtotalFormatted}
each tax in saleInvoice.taxes
tr.tax_line
td #{tax.name} [#{tax.taxRate}%]
td #{tax.taxRateAmountFormatted}
tr.total
td #{__('invoice.paper.total')}
td #{saleInvoice.totalFormatted}
tr.payment-amount
td #{__('invoice.paper.payment_amount')}
td #{saleInvoice.paymentAmountFormatted}
tr.blanace-due
td #{__('invoice.paper.balance_due')}
td #{saleInvoice.dueAmountFormatted}
div.invoice__footer
if saleInvoice.termsConditions
div.invoice__conditions
h3 #{__("invoice.paper.conditions_title")}
p #{saleInvoice.termsConditions}
if saleInvoice.invoiceMessage
div.invoice__notes
h3 #{__("invoice.paper.notes_title")}
p #{saleInvoice.invoiceMessage}

View File

@@ -0,0 +1,67 @@
extends ../PaperTemplateLayout.pug
block head
style
if (isRtl)
include ../../css/modules/payment-rtl.css
else
include ../../css/modules/payment.css
block content
div.payment
div.payment__header
div.paper
h1.title #{__("payment.paper.payment_receipt")}
if paymentReceive.paymentReceiveNo
span.paymentNumber #{paymentReceive.paymentReceiveNo}
div.organization
h3.title #{organizationName}
if organizationEmail
span.email #{organizationEmail}
div.payment__received-amount
div.label #{__('payment.paper.amount_received')}
div.amount #{paymentReceive.formattedAmount}
div.payment__meta
div.payment__meta-item.payment__meta-item--billed-to
span.label #{__("payment.paper.billed_to")}
span.value #{paymentReceive.customer.displayName}
div.payment__meta-item.payment__meta-item--payment-date
span.label #{__("payment.paper.payment_date")}
span.value #{paymentReceive.formattedPaymentDate}
div.payment__table
table
thead
tr
th.item #{__("payment.paper.invoice_number")}
th.date #{__("payment.paper.invoice_date")}
th.invoiceAmount #{__("payment.paper.invoice_amount")}
th.paymentAmount #{__("payment.paper.payment_amount")}
tbody
each entry in paymentReceive.entries
tr
td.item=entry.invoice.invoiceNo
td.date=entry.invoice.invoiceDateFormatted
td.invoiceAmount=entry.invoice.totalFormatted
td.paymentAmount=entry.invoice.paymentAmountFormatted
div.payment__table-after
div.payment__table-total
table
tbody
tr.payment-amount
td #{__('payment.paper.payment_amount')}
td #{paymentReceive.formattedAmount}
tr.blanace-due
td #{__('payment.paper.balance_due')}
td #{paymentReceive.customer.closingBalance}
div.payment__footer
if paymentReceive.statement
div.payment__notes
h3 #{__("payment.paper.statement")}
p #{paymentReceive.statement}

View File

@@ -0,0 +1,77 @@
extends ../PaperTemplateLayout.pug
block head
style
if (isRtl)
include ../../css/modules/receipt-rtl.css
else
include ../../css/modules/receipt.css
block content
div.receipt
div.receipt__header
div.paper
h1.title #{__("receipt.paper.receipt")}
span.receiptNumber #{saleReceipt.receiptNumber}
div.organization
h3.title #{organizationName}
div.receipt__receipt-amount
div.label #{__('receipt.paper.receipt_amount')}
div.amount #{saleReceipt.formattedAmount}
div.receipt__meta
div.receipt__meta-item.receipt__meta-item--billed-to
span.label #{__("receipt.paper.billed_to")}
span.value #{saleReceipt.customer.displayName}
div.receipt__meta-item.receipt__meta-item--invoice-date
span.label #{__("receipt.paper.receipt_date")}
span.value #{saleReceipt.formattedReceiptDate}
if saleReceipt.receiptNumber
div.receipt__meta-item.receipt__meta-item--invoice-number
span.label #{__("receipt.paper.receipt_number")}
span.value #{saleReceipt.receiptNumber}
div.receipt__table
table
thead
tr
th.item #{__("item_entry.paper.item_name")}
th.rate #{__("item_entry.paper.rate")}
th.quantity #{__("item_entry.paper.quantity")}
th.total #{__("item_entry.paper.total")}
tbody
each entry in saleReceipt.entries
tr
td.item=entry.item.name
td.rate=entry.rate
td.quantity=entry.quantity
td.total=entry.amount
div.receipt__table-after
div.receipt__table-total
table
tbody
tr.total
td #{__('receipt.paper.total')}
td #{saleReceipt.formattedAmount}
tr.payment-amount
td #{__('receipt.paper.payment_amount')}
td #{saleReceipt.formattedAmount}
tr.blanace-due
td #{__('receipt.paper.balance_due')}
td #{'$0'}
div.receipt__footer
if saleReceipt.statement
div.receipt__conditions
h3 #{__("receipt.paper.statement")}
p #{saleReceipt.statement}
if saleReceipt.receiptMessage
div.receipt__notes
h3 #{__("receipt.paper.notes")}
p #{saleReceipt.receiptMessage}

View File

@@ -5,7 +5,14 @@ import { body, param } from 'express-validator';
import BaseController from '@/api/controllers/BaseController'; import BaseController from '@/api/controllers/BaseController';
import { AttachmentsApplication } from '@/services/Attachments/AttachmentsApplication'; import { AttachmentsApplication } from '@/services/Attachments/AttachmentsApplication';
import { AttachmentUploadPipeline } from '@/services/Attachments/S3UploadPipeline'; import { AttachmentUploadPipeline } from '@/services/Attachments/S3UploadPipeline';
import {
ApiOperation,
ApiResponse,
ApiTags,
Route,
} from '@/decorators/swagger-decorators';
@ApiTags('Attachments')
@Service() @Service()
export class AttachmentsController extends BaseController { export class AttachmentsController extends BaseController {
@Inject() @Inject()
@@ -121,6 +128,26 @@ export class AttachmentsController extends BaseController {
* @param {NextFunction} next * @param {NextFunction} next
* @returns {Promise<Response|void>} * @returns {Promise<Response|void>}
*/ */
@ApiResponse({
status: 200,
description: 'Details of the given attachement',
schema: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
email: { type: 'string' },
},
},
},
})
@ApiOperation({
summary: 'Retrieve a specific details of attachment',
description: 'Get all registered users',
})
@Route('/attachments/:id')
private async getAttachment( private async getAttachment(
req: Request, req: Request,
res: Response, res: Response,

View File

@@ -8,6 +8,11 @@ import { IItemDTO, ItemAction, AbilitySubject } from '@/interfaces';
import { DATATYPES_LENGTH } from '@/data/DataTypes'; import { DATATYPES_LENGTH } from '@/data/DataTypes';
import CheckAbilities from '@/api/middleware/CheckPolicies'; import CheckAbilities from '@/api/middleware/CheckPolicies';
import { ItemsApplication } from '@/services/Items/ItemsApplication'; import { ItemsApplication } from '@/services/Items/ItemsApplication';
import {
ApiOperation,
ApiResponse,
Route,
} from '@/decorators/swagger-decorators';
@Service() @Service()
export default class ItemsController extends BaseController { export default class ItemsController extends BaseController {
@@ -198,6 +203,22 @@ export default class ItemsController extends BaseController {
* @param {Request} req * @param {Request} req
* @param {Response} res * @param {Response} res
*/ */
@ApiResponse({
status: 200,
description: 'Details of the given attachement',
schema: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
},
},
})
@ApiOperation({
summary: 'Creates a new item (inventory or service)',
description: 'Get all registered users',
})
@Route('/items')
private async newItem(req: Request, res: Response, next: NextFunction) { private async newItem(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req; const { tenantId } = req;
const itemDTO: IItemDTO = this.matchedBodyData(req); const itemDTO: IItemDTO = this.matchedBodyData(req);

View File

@@ -18,7 +18,7 @@ import BaseController from '@/api/controllers/BaseController';
@Service() @Service()
export default class OrganizationController extends BaseController { export default class OrganizationController extends BaseController {
@Inject() @Inject()
private organizationService: OrganizationService; organizationService: OrganizationService;
/** /**
* Router constructor. * Router constructor.
@@ -56,10 +56,10 @@ export default class OrganizationController extends BaseController {
} }
/** /**
* Build organization validation schema. * Organization setup schema.
* @returns {ValidationChain[]} * @return {ValidationChain[]}
*/ */
private get buildOrganizationValidationSchema(): ValidationChain[] { private get commonOrganizationValidationSchema(): ValidationChain[] {
return [ return [
check('name').exists().trim(), check('name').exists().trim(),
check('industry').optional({ nullable: true }).isString().trim(), check('industry').optional({ nullable: true }).isString().trim(),
@@ -72,34 +72,21 @@ export default class OrganizationController extends BaseController {
]; ];
} }
/**
* Build organization validation schema.
* @returns {ValidationChain[]}
*/
private get buildOrganizationValidationSchema(): ValidationChain[] {
return [...this.commonOrganizationValidationSchema];
}
/** /**
* Update organization validation schema. * Update organization validation schema.
* @returns {ValidationChain[]} * @returns {ValidationChain[]}
*/ */
private get updateOrganizationValidationSchema(): ValidationChain[] { private get updateOrganizationValidationSchema(): ValidationChain[] {
return [ return [
// # Profile ...this.commonOrganizationValidationSchema,
check('name').optional().trim(),
check('industry').optional({ nullable: true }).isString().trim(),
check('location').optional().isString().isISO31661Alpha2(),
check('base_currency').optional().isISO4217(),
check('timezone').optional().isIn(moment.tz.names()),
check('fiscal_year').optional().isIn(MONTHS),
check('language').optional().isString().isIn(ACCEPTED_LOCALES),
check('date_format').optional().isIn(DATE_FORMATS),
// # Address
check('address.address_1').optional().isString().trim(),
check('address.address_2').optional().isString().trim(),
check('address.postal_code').optional().isString().trim(),
check('address.city').optional().isString().trim(),
check('address.state_province').optional().isString().trim(),
check('address.phone').optional().isString().trim(),
// # Branding
check('primary_color').optional({ nullable: true }).isHexColor().trim(),
check('logo_key').optional({ nullable: true }).isString().trim(),
check('tax_number').optional({ nullable: true }).isString().trim(), check('tax_number').optional({ nullable: true }).isString().trim(),
]; ];
} }
@@ -169,7 +156,7 @@ export default class OrganizationController extends BaseController {
next: NextFunction next: NextFunction
) { ) {
const { tenantId } = req; const { tenantId } = req;
const tenantDTO = this.matchedBodyData(req, { includeOptionals: false }); const tenantDTO = this.matchedBodyData(req);
try { try {
await this.organizationService.updateOrganization(tenantId, tenantDTO); await this.organizationService.updateOrganization(tenantId, tenantDTO);

View File

@@ -1,179 +0,0 @@
import { Service, Inject } from 'typedi';
import { Request, Response, Router, NextFunction } from 'express';
import { body, param } from 'express-validator';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import BaseController from '@/api/controllers/BaseController';
import { PaymentServicesApplication } from '@/services/PaymentServices/PaymentServicesApplication';
@Service()
export class PaymentServicesController extends BaseController {
@Inject()
private paymentServicesApp: PaymentServicesApplication;
/**
* Router constructor.
*/
router() {
const router = Router();
router.get(
'/',
asyncMiddleware(this.getPaymentServicesSpecificInvoice.bind(this))
);
router.get('/state', this.getPaymentMethodsState.bind(this));
router.get('/:paymentServiceId', this.getPaymentService.bind(this));
router.post(
'/:paymentMethodId',
[
param('paymentMethodId').exists(),
body('name').optional().isString(),
body('options.bank_account_id').optional().isNumeric(),
body('options.clearing_account_id').optional().isNumeric(),
],
this.validationResult,
asyncMiddleware(this.updatePaymentMethod.bind(this))
);
router.delete(
'/:paymentMethodId',
[param('paymentMethodId').exists()],
this.validationResult,
this.deletePaymentMethod.bind(this)
);
return router;
}
/**
* Retrieve accounts types list.
* @param {Request} req - Request.
* @param {Response} res - Response.
* @return {Promise<Response | void>}
*/
private async getPaymentServicesSpecificInvoice(
req: Request<{ invoiceId: number }>,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
try {
const paymentServices =
await this.paymentServicesApp.getPaymentServicesForInvoice(tenantId);
return res.status(200).send({ paymentServices });
} catch (error) {
next(error);
}
}
/**
* Retrieves a specific payment service.
* @param {Request} req - Request.
* @param {Response} res - Response.
* @param {NextFunction} next - Next function.
* @return {Promise<Response | void>}
*/
private async getPaymentService(
req: Request<{ paymentServiceId: number }>,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const { paymentServiceId } = req.params;
try {
const paymentService = await this.paymentServicesApp.getPaymentService(
tenantId,
paymentServiceId
);
return res.status(200).send({ data: paymentService });
} catch (error) {
next(error);
}
}
/**
* Edits the given payment method settings.
* @param {Request} req - Request.
* @param {Response} res - Response.
* @return {Promise<Response | void>}
*/
private async updatePaymentMethod(
req: Request<{ paymentMethodId: number }>,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const { paymentMethodId } = req.params;
const updatePaymentMethodDTO = this.matchedBodyData(req);
try {
await this.paymentServicesApp.editPaymentMethod(
tenantId,
paymentMethodId,
updatePaymentMethodDTO
);
return res.status(200).send({
id: paymentMethodId,
message: 'The given payment method has been updated.',
});
} catch (error) {
next(error);
}
}
/**
* Retrieves the payment state providing state.
* @param {Request} req - Request.
* @param {Response} res - Response.
* @param {NextFunction} next - Next function.
* @return {Promise<Response | void>}
*/
private async getPaymentMethodsState(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
try {
const paymentMethodsState =
await this.paymentServicesApp.getPaymentMethodsState(tenantId);
return res.status(200).send({ data: paymentMethodsState });
} catch (error) {
next(error);
}
}
/**
* Deletes the given payment method.
* @param {Request<{ paymentMethodId: number }>} req - Request.
* @param {Response} res - Response.
* @param {NextFunction} next - Next function.
* @return {Promise<Response | void>}
*/
private async deletePaymentMethod(
req: Request<{ paymentMethodId: number }>,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const { paymentMethodId } = req.params;
try {
await this.paymentServicesApp.deletePaymentMethod(
tenantId,
paymentMethodId
);
return res.status(204).send({
id: paymentMethodId,
message: 'The payment method has been deleted.',
});
} catch (error) {
next(error);
}
}
}

View File

@@ -1,195 +0,0 @@
import { Router, Request, Response, NextFunction } from 'express';
import { check, param, query } from 'express-validator';
import { Service, Inject } from 'typedi';
import BaseController from '@/api/controllers/BaseController';
import { PdfTemplateApplication } from '@/services/PdfTemplate/PdfTemplateApplication';
@Service()
export class PdfTemplatesController extends BaseController {
@Inject()
public pdfTemplateApplication: PdfTemplateApplication;
/**
* Router constructor method.
*/
public router() {
const router = Router();
router.delete(
'/:template_id',
[param('template_id').exists().isInt().toInt()],
this.validationResult,
this.deletePdfTemplate.bind(this)
);
router.post(
'/:template_id',
[
param('template_id').exists().isInt().toInt(),
check('template_name').exists(),
check('attributes').exists(),
],
this.validationResult,
this.editPdfTemplate.bind(this)
);
router.get('/state', this.getOrganizationBrandingState.bind(this));
router.get(
'/',
[query('resource').optional()],
this.validationResult,
this.getPdfTemplates.bind(this)
);
router.get(
'/:template_id',
[param('template_id').exists().isInt().toInt()],
this.validationResult,
this.getPdfTemplate.bind(this)
);
router.post(
'/',
[
check('template_name').exists(),
check('resource').exists(),
check('attributes').exists(),
],
this.validationResult,
this.createPdfInvoiceTemplate.bind(this)
);
router.post(
'/:template_id/assign_default',
[param('template_id').exists().isInt().toInt()],
this.validationResult,
this.assginPdfTemplateAsDefault.bind(this)
);
return router;
}
async createPdfInvoiceTemplate(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const { templateName, resource, attributes } = this.matchedBodyData(req);
try {
const result = await this.pdfTemplateApplication.createPdfTemplate(
tenantId,
templateName,
resource,
attributes
);
return res.status(201).send({
id: result.id,
message: 'The PDF template has been created successfully.',
});
} catch (error) {
next(error);
}
}
async editPdfTemplate(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { template_id: templateId } = req.params;
const editTemplateDTO = this.matchedBodyData(req);
try {
const result = await this.pdfTemplateApplication.editPdfTemplate(
tenantId,
Number(templateId),
editTemplateDTO
);
return res.status(200).send({
id: result.id,
message: 'The PDF template has been updated successfully.',
});
} catch (error) {
next(error);
}
}
async deletePdfTemplate(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { template_id: templateId } = req.params;
try {
await this.pdfTemplateApplication.deletePdfTemplate(
tenantId,
Number(templateId)
);
return res.status(204).send({
id: templateId,
message: 'The PDF template has been deleted successfully.',
});
} catch (error) {
next(error);
}
}
async getPdfTemplate(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { template_id: templateId } = req.params;
try {
const template = await this.pdfTemplateApplication.getPdfTemplate(
tenantId,
Number(templateId)
);
return res.status(200).send(template);
} catch (error) {
next(error);
}
}
async getPdfTemplates(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const query = this.matchedQueryData(req);
try {
const templates = await this.pdfTemplateApplication.getPdfTemplates(
tenantId,
query
);
return res.status(200).send(templates);
} catch (error) {
next(error);
}
}
async assginPdfTemplateAsDefault(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const { template_id: templateId } = req.params;
try {
await this.pdfTemplateApplication.assignPdfTemplateAsDefault(
tenantId,
Number(templateId)
);
return res.status(204).send({
id: templateId,
message: 'The given pdf template has been assigned as default template',
});
} catch (error) {
next(error);
}
}
async getOrganizationBrandingState(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
try {
const data =
await this.pdfTemplateApplication.getPdfTemplateBrandingState(tenantId);
return res.status(200).send({ data });
} catch (error) {
next(error);
}
}
}

View File

@@ -4,7 +4,6 @@ import { check, param, query } from 'express-validator';
import { import {
AbilitySubject, AbilitySubject,
BillAction, BillAction,
DiscountType,
IBillDTO, IBillDTO,
IBillEditDTO, IBillEditDTO,
} from '@/interfaces'; } from '@/interfaces';
@@ -122,16 +121,11 @@ export default class BillsController extends BaseController {
check('entries.*.index').exists().isNumeric().toInt(), check('entries.*.index').exists().isNumeric().toInt(),
check('entries.*.item_id').exists().isNumeric().toInt(), check('entries.*.item_id').exists().isNumeric().toInt(),
check('entries.*.rate').exists().isNumeric().toFloat(), check('entries.*.rate').exists().isNumeric().toFloat(),
check('entries.*.quantity').exists().isNumeric().toFloat(), check('entries.*.quantity').exists().isNumeric().toInt(),
check('entries.*.discount') check('entries.*.discount')
.optional({ nullable: true }) .optional({ nullable: true })
.isNumeric() .isNumeric()
.toFloat(), .toFloat(),
check('entries.*.discount_type')
.default(DiscountType.Percentage)
.isString()
.isIn([DiscountType.Percentage, DiscountType.Amount]),
check('entries.*.description').optional({ nullable: true }).trim(), check('entries.*.description').optional({ nullable: true }).trim(),
check('entries.*.landed_cost') check('entries.*.landed_cost')
.optional({ nullable: true }) .optional({ nullable: true })
@@ -150,18 +144,8 @@ export default class BillsController extends BaseController {
.isNumeric() .isNumeric()
.toInt(), .toInt(),
// Attachments
check('attachments').isArray().optional(), check('attachments').isArray().optional(),
check('attachments.*.key').exists().isString(), check('attachments.*.key').exists().isString(),
// # Discount
check('discount_type')
.default(DiscountType.Amount)
.isIn([DiscountType.Amount, DiscountType.Percentage]),
check('discount').optional({ nullable: true }).isDecimal().toFloat(),
// # Adjustment
check('adjustment').optional({ nullable: true }).isNumeric().toFloat(),
]; ];
} }
@@ -204,15 +188,6 @@ export default class BillsController extends BaseController {
check('attachments').isArray().optional(), check('attachments').isArray().optional(),
check('attachments.*.key').exists().isString(), check('attachments.*.key').exists().isString(),
// # Discount
check('discount_type')
.default(DiscountType.Amount)
.isIn([DiscountType.Amount, DiscountType.Percentage]),
check('discount').optional({ nullable: true }).isDecimal().toFloat(),
// # Adjustment
check('adjustment').optional({ nullable: true }).isNumeric().toFloat(),
]; ];
} }

View File

@@ -3,7 +3,6 @@ import { check, param, query } from 'express-validator';
import { Service, Inject } from 'typedi'; import { Service, Inject } from 'typedi';
import { import {
AbilitySubject, AbilitySubject,
DiscountType,
IVendorCreditCreateDTO, IVendorCreditCreateDTO,
IVendorCreditEditDTO, IVendorCreditEditDTO,
VendorCreditAction, VendorCreditAction,
@@ -171,15 +170,11 @@ export default class VendorCreditController extends BaseController {
check('entries.*.index').exists().isNumeric().toInt(), check('entries.*.index').exists().isNumeric().toInt(),
check('entries.*.item_id').exists().isNumeric().toInt(), check('entries.*.item_id').exists().isNumeric().toInt(),
check('entries.*.rate').exists().isNumeric().toFloat(), check('entries.*.rate').exists().isNumeric().toFloat(),
check('entries.*.quantity').exists().isNumeric().toFloat(), check('entries.*.quantity').exists().isNumeric().toInt(),
check('entries.*.discount') check('entries.*.discount')
.optional({ nullable: true }) .optional({ nullable: true })
.isNumeric() .isNumeric()
.toFloat(), .toFloat(),
check('entries.*.discount_type')
.default(DiscountType.Percentage)
.isString()
.isIn([DiscountType.Percentage, DiscountType.Amount]),
check('entries.*.description').optional({ nullable: true }).trim(), check('entries.*.description').optional({ nullable: true }).trim(),
check('entries.*.warehouse_id') check('entries.*.warehouse_id')
.optional({ nullable: true }) .optional({ nullable: true })
@@ -188,16 +183,6 @@ export default class VendorCreditController extends BaseController {
check('attachments').isArray().optional(), check('attachments').isArray().optional(),
check('attachments.*.key').exists().isString(), check('attachments.*.key').exists().isString(),
// Discount.
check('discount').optional({ nullable: true }).isNumeric().toFloat(),
check('discount_type')
.optional({ nullable: true })
.isString()
.isIn([DiscountType.Percentage, DiscountType.Amount]),
// Adjustment.
check('adjustment').optional({ nullable: true }).isNumeric().toFloat(),
]; ];
} }
@@ -224,15 +209,11 @@ export default class VendorCreditController extends BaseController {
check('entries.*.index').exists().isNumeric().toInt(), check('entries.*.index').exists().isNumeric().toInt(),
check('entries.*.item_id').exists().isNumeric().toInt(), check('entries.*.item_id').exists().isNumeric().toInt(),
check('entries.*.rate').exists().isNumeric().toFloat(), check('entries.*.rate').exists().isNumeric().toFloat(),
check('entries.*.quantity').exists().isNumeric().toFloat(), check('entries.*.quantity').exists().isNumeric().toInt(),
check('entries.*.discount') check('entries.*.discount')
.optional({ nullable: true }) .optional({ nullable: true })
.isNumeric() .isNumeric()
.toFloat(), .toFloat(),
check('entries.*.discount_type')
.default(DiscountType.Percentage)
.isString()
.isIn([DiscountType.Percentage, DiscountType.Amount]),
check('entries.*.description').optional({ nullable: true }).trim(), check('entries.*.description').optional({ nullable: true }).trim(),
check('entries.*.warehouse_id') check('entries.*.warehouse_id')
.optional({ nullable: true }) .optional({ nullable: true })
@@ -241,16 +222,6 @@ export default class VendorCreditController extends BaseController {
check('attachments').isArray().optional(), check('attachments').isArray().optional(),
check('attachments.*.key').exists().isString(), check('attachments.*.key').exists().isString(),
// Discount.
check('discount').optional({ nullable: true }).isNumeric().toFloat(),
check('discount_type')
.optional({ nullable: true })
.isString()
.isIn([DiscountType.Percentage, DiscountType.Amount]),
// Adjustment.
check('adjustment').optional({ nullable: true }).isNumeric().toFloat(),
]; ];
} }

View File

@@ -4,7 +4,6 @@ import { Inject, Service } from 'typedi';
import { import {
AbilitySubject, AbilitySubject,
CreditNoteAction, CreditNoteAction,
DiscountType,
ICreditNoteEditDTO, ICreditNoteEditDTO,
ICreditNoteNewDTO, ICreditNoteNewDTO,
} from '@/interfaces'; } from '@/interfaces';
@@ -28,7 +27,6 @@ import GetCreditNoteAssociatedAppliedInvoices from '@/services/CreditNotes/GetCr
import GetRefundCreditTransaction from '@/services/CreditNotes/GetRefundCreditNoteTransaction'; import GetRefundCreditTransaction from '@/services/CreditNotes/GetRefundCreditNoteTransaction';
import GetCreditNotePdf from '../../../services/CreditNotes/GetCreditNotePdf'; import GetCreditNotePdf from '../../../services/CreditNotes/GetCreditNotePdf';
import { ACCEPT_TYPE } from '@/interfaces/Http'; import { ACCEPT_TYPE } from '@/interfaces/Http';
import { GetCreditNoteState } from '@/services/CreditNotes/GetCreditNoteState';
/** /**
* Credit notes controller. * Credit notes controller.
* @service * @service
@@ -83,9 +81,6 @@ export default class PaymentReceivesController extends BaseController {
@Inject() @Inject()
creditNotePdf: GetCreditNotePdf; creditNotePdf: GetCreditNotePdf;
@Inject()
getCreditNoteStateService: GetCreditNoteState;
/** /**
* Router constructor. * Router constructor.
*/ */
@@ -110,12 +105,6 @@ export default class PaymentReceivesController extends BaseController {
this.asyncMiddleware(this.newCreditNote), this.asyncMiddleware(this.newCreditNote),
this.handleServiceErrors this.handleServiceErrors
); );
router.get(
'/state',
CheckPolicies(CreditNoteAction.View, AbilitySubject.CreditNote),
this.asyncMiddleware(this.getCreditNoteState.bind(this)),
this.handleServiceErrors
);
// Get specific credit note. // Get specific credit note.
router.get( router.get(
'/:id', '/:id',
@@ -234,37 +223,19 @@ export default class PaymentReceivesController extends BaseController {
check('entries.*.index').exists().isNumeric().toInt(), check('entries.*.index').exists().isNumeric().toInt(),
check('entries.*.item_id').exists().isNumeric().toInt(), check('entries.*.item_id').exists().isNumeric().toInt(),
check('entries.*.rate').exists().isNumeric().toFloat(), check('entries.*.rate').exists().isNumeric().toFloat(),
check('entries.*.quantity').exists().isNumeric().toFloat(), check('entries.*.quantity').exists().isNumeric().toInt(),
check('entries.*.discount') check('entries.*.discount')
.optional({ nullable: true }) .optional({ nullable: true })
.isNumeric() .isNumeric()
.toFloat(), .toFloat(),
check('entries.*.discount_type')
.default(DiscountType.Percentage)
.isString()
.isIn([DiscountType.Percentage, DiscountType.Amount]),
check('entries.*.description').optional({ nullable: true }).trim(), check('entries.*.description').optional({ nullable: true }).trim(),
check('entries.*.warehouse_id') check('entries.*.warehouse_id')
.optional({ nullable: true }) .optional({ nullable: true })
.isNumeric() .isNumeric()
.toInt(), .toInt(),
// Attachments.
check('attachments').isArray().optional(), check('attachments').isArray().optional(),
check('attachments.*.key').exists().isString(), check('attachments.*.key').exists().isString(),
// Pdf template id.
check('pdf_template_id').optional({ nullable: true }).isNumeric().toInt(),
// Discount.
check('discount').optional({ nullable: true }).isNumeric().toFloat(),
check('discount_type')
.optional({ nullable: true })
.isString()
.isIn([DiscountType.Percentage, DiscountType.Amount]),
// Adjustment.
check('adjustment').optional({ nullable: true }).isNumeric().toFloat(),
]; ];
} }
@@ -487,14 +458,13 @@ export default class PaymentReceivesController extends BaseController {
ACCEPT_TYPE.APPLICATION_PDF, ACCEPT_TYPE.APPLICATION_PDF,
]); ]);
if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) { if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
const [pdfContent, filename] = await this.creditNotePdf.getCreditNotePdf( const pdfContent = await this.creditNotePdf.getCreditNotePdf(
tenantId, tenantId,
creditNoteId creditNoteId
); );
res.set({ res.set({
'Content-Type': 'application/pdf', 'Content-Type': 'application/pdf',
'Content-Length': pdfContent.length, 'Content-Length': pdfContent.length,
'Content-Disposition': `attachment; filename="${filename}"`,
}); });
res.send(pdfContent); res.send(pdfContent);
} else { } else {
@@ -763,23 +733,6 @@ export default class PaymentReceivesController extends BaseController {
} }
}; };
private getCreditNoteState = async (
req: Request,
res: Response,
next: NextFunction
) => {
const { tenantId } = req;
try {
const data = await this.getCreditNoteStateService.getCreditNoteState(
tenantId
);
return res.status(200).send({ data });
} catch (error) {
next(error);
}
};
/** /**
* Handles service errors. * Handles service errors.
* @param {Error} error * @param {Error} error

View File

@@ -95,12 +95,6 @@ export default class PaymentReceivesController extends BaseController {
asyncMiddleware(this.getPaymentReceiveInvoices.bind(this)), asyncMiddleware(this.getPaymentReceiveInvoices.bind(this)),
this.handleServiceErrors this.handleServiceErrors
); );
router.get(
'/state',
CheckPolicies(PaymentReceiveAction.View, AbilitySubject.PaymentReceive),
this.getPaymentReceivedState.bind(this),
this.handleServiceErrors
);
router.get( router.get(
'/:id', '/:id',
CheckPolicies(PaymentReceiveAction.View, AbilitySubject.PaymentReceive), CheckPolicies(PaymentReceiveAction.View, AbilitySubject.PaymentReceive),
@@ -130,18 +124,9 @@ export default class PaymentReceivesController extends BaseController {
[ [
...this.paymentReceiveValidation, ...this.paymentReceiveValidation,
body('subject').isString().optional(), body('subject').isString().optional(),
body('from').isString().optional(), body('from').isString().optional(),
body('to').isString().optional(),
body('to').isArray().exists(), body('body').isString().optional(),
body('to.*').isString().isEmail().optional(),
body('cc').isArray().optional({ nullable: true }),
body('cc.*').isString().isEmail().optional(),
body('bcc').isArray().optional({ nullable: true }),
body('bcc.*').isString().isEmail().optional(),
body('attach_invoice').optional().isBoolean().toBoolean(), body('attach_invoice').optional().isBoolean().toBoolean(),
], ],
this.sendPaymentReceiveByMail.bind(this), this.sendPaymentReceiveByMail.bind(this),
@@ -182,9 +167,6 @@ export default class PaymentReceivesController extends BaseController {
check('attachments').isArray().optional(), check('attachments').isArray().optional(),
check('attachments.*.key').exists().isString(), check('attachments.*.key').exists().isString(),
// Pdf template id.
check('pdf_template_id').optional({ nullable: true }).isNumeric().toInt(),
]; ];
} }
@@ -406,29 +388,6 @@ export default class PaymentReceivesController extends BaseController {
} }
} }
/**
*
* @async
* @param {Request} req -
* @param {Response} res -
*/
private async getPaymentReceivedState(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
try {
const data = await this.paymentReceiveApplication.getPaymentReceivedState(
tenantId
);
return res.status(200).send({ data });
} catch (error) {
next(error);
}
}
/** /**
* Retrieve the given payment receive details. * Retrieve the given payment receive details.
* @async * @async
@@ -479,11 +438,10 @@ export default class PaymentReceivesController extends BaseController {
const acceptType = accept.types([ const acceptType = accept.types([
ACCEPT_TYPE.APPLICATION_JSON, ACCEPT_TYPE.APPLICATION_JSON,
ACCEPT_TYPE.APPLICATION_PDF, ACCEPT_TYPE.APPLICATION_PDF,
ACCEPT_TYPE.APPLICATION_TEXT_HTML,
]); ]);
// Responds pdf format. // Response in pdf format.
if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) { if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
const [pdfContent, filename] = const pdfContent =
await this.paymentReceiveApplication.getPaymentReceivePdf( await this.paymentReceiveApplication.getPaymentReceivePdf(
tenantId, tenantId,
paymentReceiveId paymentReceiveId
@@ -491,18 +449,9 @@ export default class PaymentReceivesController extends BaseController {
res.set({ res.set({
'Content-Type': 'application/pdf', 'Content-Type': 'application/pdf',
'Content-Length': pdfContent.length, 'Content-Length': pdfContent.length,
'Content-Disposition': `attachment; filename="${filename}"`,
}); });
res.send(pdfContent); res.send(pdfContent);
// Responds html format. // Response in json format.
} else if (ACCEPT_TYPE.APPLICATION_TEXT_HTML === acceptType) {
const htmlContent =
await this.paymentReceiveApplication.getPaymentReceivedHtml(
tenantId,
paymentReceiveId
);
return res.status(200).send({ htmlContent });
// Responds json format.
} else { } else {
const paymentReceive = const paymentReceive =
await this.paymentReceiveApplication.getPaymentReceive( await this.paymentReceiveApplication.getPaymentReceive(

View File

@@ -3,7 +3,6 @@ import { body, check, param, query } from 'express-validator';
import { Inject, Service } from 'typedi'; import { Inject, Service } from 'typedi';
import { import {
AbilitySubject, AbilitySubject,
DiscountType,
ISaleEstimateDTO, ISaleEstimateDTO,
SaleEstimateAction, SaleEstimateAction,
SaleEstimateMailOptionsDTO, SaleEstimateMailOptionsDTO,
@@ -14,8 +13,11 @@ import DynamicListingService from '@/services/DynamicListing/DynamicListService'
import { ServiceError } from '@/exceptions'; import { ServiceError } from '@/exceptions';
import CheckPolicies from '@/api/middleware/CheckPolicies'; import CheckPolicies from '@/api/middleware/CheckPolicies';
import { SaleEstimatesApplication } from '@/services/Sales/Estimates/SaleEstimatesApplication'; import { SaleEstimatesApplication } from '@/services/Sales/Estimates/SaleEstimatesApplication';
import { ACCEPT_TYPE } from '@/interfaces/Http';
const ACCEPT_TYPE = {
APPLICATION_PDF: 'application/pdf',
APPLICATION_JSON: 'application/json',
};
@Service() @Service()
export default class SalesEstimatesController extends BaseController { export default class SalesEstimatesController extends BaseController {
@Inject() @Inject()
@@ -49,7 +51,7 @@ export default class SalesEstimatesController extends BaseController {
router.post( router.post(
'/:id/approve', '/:id/approve',
CheckPolicies(SaleEstimateAction.Edit, AbilitySubject.SaleEstimate), CheckPolicies(SaleEstimateAction.Edit, AbilitySubject.SaleEstimate),
[...this.validateSpecificEstimateSchema], [this.validateSpecificEstimateSchema],
this.validationResult, this.validationResult,
asyncMiddleware(this.approveSaleEstimate.bind(this)), asyncMiddleware(this.approveSaleEstimate.bind(this)),
this.handleServiceErrors this.handleServiceErrors
@@ -57,7 +59,7 @@ export default class SalesEstimatesController extends BaseController {
router.post( router.post(
'/:id/reject', '/:id/reject',
CheckPolicies(SaleEstimateAction.Edit, AbilitySubject.SaleEstimate), CheckPolicies(SaleEstimateAction.Edit, AbilitySubject.SaleEstimate),
[...this.validateSpecificEstimateSchema], [this.validateSpecificEstimateSchema],
this.validationResult, this.validationResult,
asyncMiddleware(this.rejectSaleEstimate.bind(this)), asyncMiddleware(this.rejectSaleEstimate.bind(this)),
this.handleServiceErrors this.handleServiceErrors
@@ -103,12 +105,6 @@ export default class SalesEstimatesController extends BaseController {
asyncMiddleware(this.deleteEstimate.bind(this)), asyncMiddleware(this.deleteEstimate.bind(this)),
this.handleServiceErrors this.handleServiceErrors
); );
router.get(
'/state',
CheckPolicies(SaleEstimateAction.View, AbilitySubject.SaleEstimate),
this.getSaleEstimateState.bind(this),
this.handleServiceErrors
);
router.get( router.get(
'/:id', '/:id',
CheckPolicies(SaleEstimateAction.View, AbilitySubject.SaleEstimate), CheckPolicies(SaleEstimateAction.View, AbilitySubject.SaleEstimate),
@@ -131,18 +127,8 @@ export default class SalesEstimatesController extends BaseController {
[ [
...this.validateSpecificEstimateSchema, ...this.validateSpecificEstimateSchema,
body('subject').isString().optional(), body('subject').isString().optional(),
body('from').isString().optional(), body('from').isString().optional(),
body('to').isString().optional(),
body('to').isArray().exists(),
body('to.*').isString().isEmail().optional(),
body('cc').isArray().optional({ nullable: true }),
body('cc.*').isString().isEmail().optional(),
body('bcc').isArray().optional({ nullable: true }),
body('bcc.*').isString().isEmail().optional(),
body('body').isString().optional(), body('body').isString().optional(),
body('attach_invoice').optional().isBoolean().toBoolean(), body('attach_invoice').optional().isBoolean().toBoolean(),
], ],
@@ -151,10 +137,10 @@ export default class SalesEstimatesController extends BaseController {
this.handleServiceErrors this.handleServiceErrors
); );
router.get( router.get(
'/:id/mail/state', '/:id/mail',
[...this.validateSpecificEstimateSchema], [...this.validateSpecificEstimateSchema],
this.validationResult, this.validationResult,
asyncMiddleware(this.getSaleEstimateMailState.bind(this)), asyncMiddleware(this.getSaleEstimateMail.bind(this)),
this.handleServiceErrors this.handleServiceErrors
); );
return router; return router;
@@ -180,18 +166,15 @@ export default class SalesEstimatesController extends BaseController {
check('entries').exists().isArray({ min: 1 }), check('entries').exists().isArray({ min: 1 }),
check('entries.*.index').exists().isNumeric().toInt(), check('entries.*.index').exists().isNumeric().toInt(),
check('entries.*.item_id').exists().isNumeric().toInt(), check('entries.*.item_id').exists().isNumeric().toInt(),
check('entries.*.quantity').exists().isNumeric().toFloat(), check('entries.*.quantity').exists().isNumeric().toInt(),
check('entries.*.rate').exists().isNumeric().toFloat(), check('entries.*.rate').exists().isNumeric().toFloat(),
check('entries.*.description').optional({ nullable: true }).trim(), check('entries.*.description')
.optional({ nullable: true })
.trim(),
check('entries.*.discount') check('entries.*.discount')
.optional({ nullable: true }) .optional({ nullable: true })
.isNumeric() .isNumeric()
.toFloat(), .toFloat(),
check('entries.*.discount_type')
.default(DiscountType.Percentage)
.isString()
.isIn([DiscountType.Percentage, DiscountType.Amount]),
check('entries.*.warehouse_id') check('entries.*.warehouse_id')
.optional({ nullable: true }) .optional({ nullable: true })
.isNumeric() .isNumeric()
@@ -201,21 +184,8 @@ export default class SalesEstimatesController extends BaseController {
check('terms_conditions').optional().trim(), check('terms_conditions').optional().trim(),
check('send_to_email').optional().trim(), check('send_to_email').optional().trim(),
// # Attachments
check('attachments').isArray().optional(), check('attachments').isArray().optional(),
check('attachments.*.key').exists().isString(), check('attachments.*.key').exists().isString(),
// # Pdf template id.
check('pdf_template_id').optional({ nullable: true }).isNumeric().toInt(),
// # Discount
check('discount').optional({ nullable: true }).isNumeric().toFloat(),
check('discount_type')
.default(DiscountType.Amount)
.isIn([DiscountType.Amount, DiscountType.Percentage]),
// # Adjustment
check('adjustment').optional({ nullable: true }).isNumeric().toFloat(),
]; ];
} }
@@ -418,30 +388,20 @@ export default class SalesEstimatesController extends BaseController {
const acceptType = accept.types([ const acceptType = accept.types([
ACCEPT_TYPE.APPLICATION_JSON, ACCEPT_TYPE.APPLICATION_JSON,
ACCEPT_TYPE.APPLICATION_PDF, ACCEPT_TYPE.APPLICATION_PDF,
ACCEPT_TYPE.APPLICATION_TEXT_HTML,
]); ]);
// Retrieves estimate in pdf format. // Retrieves estimate in pdf format.
if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) { if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) {
const [pdfContent, filename] = const pdfContent = await this.saleEstimatesApplication.getSaleEstimatePdf(
await this.saleEstimatesApplication.getSaleEstimatePdf( tenantId,
tenantId, estimateId
estimateId );
);
res.set({ res.set({
'Content-Type': 'application/pdf', 'Content-Type': 'application/pdf',
'Content-Length': pdfContent.length, 'Content-Length': pdfContent.length,
'Content-Disposition': `attachment; filename="${filename}"`,
}); });
res.send(pdfContent); res.send(pdfContent);
// Retrieves estimates in json format. // Retrieves estimates in json format.
} else if (ACCEPT_TYPE.APPLICATION_TEXT_HTML === acceptType) { } else {
const htmlContent =
await this.saleEstimatesApplication.getSaleEstimateHtml(
tenantId,
estimateId
);
return res.status(200).send({ htmlContent });
} else if (ACCEPT_TYPE.APPLICATION_JSON) {
const estimate = await this.saleEstimatesApplication.getSaleEstimate( const estimate = await this.saleEstimatesApplication.getSaleEstimate(
tenantId, tenantId,
estimateId estimateId
@@ -566,35 +526,18 @@ export default class SalesEstimatesController extends BaseController {
* @param {Response} res * @param {Response} res
* @param {NextFunction} next * @param {NextFunction} next
*/ */
private getSaleEstimateMailState = async ( private getSaleEstimateMail = async (
req: Request<{ id: number }>,
res: Response,
next: NextFunction
) => {
const { tenantId } = req;
const { id: estimateId } = req.params;
try {
const data = await this.saleEstimatesApplication.getSaleEstimateMailState(
tenantId,
estimateId
);
return res.status(200).send({ data });
} catch (error) {
next(error);
}
};
private getSaleEstimateState = async (
req: Request, req: Request,
res: Response, res: Response,
next: NextFunction next: NextFunction
) => { ) => {
const { tenantId } = req; const { tenantId } = req;
const { id: invoiceId } = req.params;
try { try {
const data = await this.saleEstimatesApplication.getSaleEstimateState( const data = await this.saleEstimatesApplication.getSaleEstimateMail(
tenantId tenantId,
invoiceId
); );
return res.status(200).send({ data }); return res.status(200).send({ data });
} catch (error) { } catch (error) {

View File

@@ -11,7 +11,6 @@ import {
SaleInvoiceAction, SaleInvoiceAction,
AbilitySubject, AbilitySubject,
SendInvoiceMailDTO, SendInvoiceMailDTO,
DiscountType,
} from '@/interfaces'; } from '@/interfaces';
import CheckPolicies from '@/api/middleware/CheckPolicies'; import CheckPolicies from '@/api/middleware/CheckPolicies';
import { SaleInvoiceApplication } from '@/services/Sales/Invoices/SaleInvoicesApplication'; import { SaleInvoiceApplication } from '@/services/Sales/Invoices/SaleInvoicesApplication';
@@ -131,12 +130,6 @@ export default class SaleInvoicesController extends BaseController {
this.asyncMiddleware(this.getInvoicePaymentTransactions), this.asyncMiddleware(this.getInvoicePaymentTransactions),
this.handleServiceErrors this.handleServiceErrors
); );
router.get(
'/state',
CheckPolicies(SaleInvoiceAction.View, AbilitySubject.SaleInvoice),
asyncMiddleware(this.getSaleInvoiceState.bind(this)),
this.handleServiceErrors
);
router.get( router.get(
'/:id', '/:id',
CheckPolicies(SaleInvoiceAction.View, AbilitySubject.SaleInvoice), CheckPolicies(SaleInvoiceAction.View, AbilitySubject.SaleInvoice),
@@ -145,7 +138,6 @@ export default class SaleInvoicesController extends BaseController {
asyncMiddleware(this.getSaleInvoice.bind(this)), asyncMiddleware(this.getSaleInvoice.bind(this)),
this.handleServiceErrors this.handleServiceErrors
); );
router.get( router.get(
'/', '/',
CheckPolicies(SaleInvoiceAction.View, AbilitySubject.SaleInvoice), CheckPolicies(SaleInvoiceAction.View, AbilitySubject.SaleInvoice),
@@ -180,21 +172,10 @@ export default class SaleInvoicesController extends BaseController {
'/:id/mail', '/:id/mail',
[ [
...this.specificSaleInvoiceValidation, ...this.specificSaleInvoiceValidation,
body('subject').isString().optional(),
body('subject').isString().optional({ nullable: true }),
body('message').isString().optional({ nullable: true }),
body('from').isString().optional(), body('from').isString().optional(),
body('to').isString().optional(),
body('to').isArray().exists(), body('body').isString().optional(),
body('to.*').isString().isEmail().optional(),
body('cc').isArray().optional({ nullable: true }),
body('cc.*').isString().isEmail().optional(),
body('bcc').isArray().optional({ nullable: true }),
body('bcc.*').isString().isEmail().optional(),
body('attach_invoice').optional().isBoolean().toBoolean(), body('attach_invoice').optional().isBoolean().toBoolean(),
], ],
this.validationResult, this.validationResult,
@@ -202,7 +183,7 @@ export default class SaleInvoicesController extends BaseController {
this.handleServiceErrors this.handleServiceErrors
); );
router.get( router.get(
'/:id/mail/state', '/:id/mail',
[...this.specificSaleInvoiceValidation], [...this.specificSaleInvoiceValidation],
this.validationResult, this.validationResult,
asyncMiddleware(this.getSaleInvoiceMail.bind(this)), asyncMiddleware(this.getSaleInvoiceMail.bind(this)),
@@ -243,11 +224,9 @@ export default class SaleInvoicesController extends BaseController {
.optional({ nullable: true }) .optional({ nullable: true })
.isNumeric() .isNumeric()
.toFloat(), .toFloat(),
check('entries.*.discount_type') check('entries.*.description')
.default(DiscountType.Percentage) .optional({ nullable: true })
.isString() .trim(),
.isIn([DiscountType.Percentage, DiscountType.Amount]),
check('entries.*.description').optional({ nullable: true }).trim(),
check('entries.*.tax_code') check('entries.*.tax_code')
.optional({ nullable: true }) .optional({ nullable: true })
.trim() .trim()
@@ -278,24 +257,6 @@ export default class SaleInvoicesController extends BaseController {
.optional({ nullable: true }) .optional({ nullable: true })
.isNumeric() .isNumeric()
.toFloat(), .toFloat(),
// Pdf template id.
check('pdf_template_id').optional({ nullable: true }).isNumeric().toInt(),
// Payment methods.
check('payment_methods').optional({ nullable: true }).isArray(),
check('payment_methods.*.payment_integration_id').exists().toInt(),
check('payment_methods.*.enable').exists().isBoolean(),
// Discount
check('discount').optional({ nullable: true }).isNumeric().toFloat(),
check('discount_type')
.optional({ nullable: true })
.isString()
.isIn([DiscountType.Percentage, DiscountType.Amount]),
// Adjustments
check('adjustment').optional({ nullable: true }).isNumeric().toFloat(),
]; ];
} }
@@ -464,28 +425,19 @@ export default class SaleInvoicesController extends BaseController {
const acceptType = accept.types([ const acceptType = accept.types([
ACCEPT_TYPE.APPLICATION_JSON, ACCEPT_TYPE.APPLICATION_JSON,
ACCEPT_TYPE.APPLICATION_PDF, ACCEPT_TYPE.APPLICATION_PDF,
ACCEPT_TYPE.APPLICATION_TEXT_HTML,
]); ]);
// Retrieves invoice in PDF format. // Retrieves invoice in pdf format.
if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) { if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) {
const [pdfContent, filename] = const pdfContent = await this.saleInvoiceApplication.saleInvoicePdf(
await this.saleInvoiceApplication.saleInvoicePdf(
tenantId,
saleInvoiceId
);
res.set({
'Content-Type': 'application/pdf',
'Content-Length': pdfContent.length,
'Content-Disposition': `attachment; filename="${filename}"`,
});
res.send(pdfContent);
// Retrieves invoice in html json format.
} else if (ACCEPT_TYPE.APPLICATION_TEXT_HTML === acceptType) {
const htmlContent = await this.saleInvoiceApplication.saleInvoiceHtml(
tenantId, tenantId,
saleInvoiceId saleInvoiceId
); );
return res.status(200).send({ htmlContent }); res.set({
'Content-Type': 'application/pdf',
'Content-Length': pdfContent.length,
});
res.send(pdfContent);
// Retrieves invoice in json format.
} else { } else {
const saleInvoice = await this.saleInvoiceApplication.getSaleInvoice( const saleInvoice = await this.saleInvoiceApplication.getSaleInvoice(
tenantId, tenantId,
@@ -495,24 +447,6 @@ export default class SaleInvoicesController extends BaseController {
return res.status(200).send({ saleInvoice }); return res.status(200).send({ saleInvoice });
} }
} }
private async getSaleInvoiceState(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
try {
const data = await this.saleInvoiceApplication.getSaleInvoiceState(
tenantId
);
return res.status(200).send({ data });
} catch (error) {
next(error);
}
}
/** /**
* Retrieve paginated sales invoices with custom view metadata. * Retrieve paginated sales invoices with custom view metadata.
* @param {Request} req * @param {Request} req
@@ -811,7 +745,7 @@ export default class SaleInvoicesController extends BaseController {
} }
/** /**
* Retrieves the mail state of the given sale invoice. * Retrieves the default mail options of the given sale invoice.
* @param {Request} req * @param {Request} req
* @param {Response} res * @param {Response} res
* @param {NextFunction} next * @param {NextFunction} next
@@ -825,7 +759,7 @@ export default class SaleInvoicesController extends BaseController {
const { id: invoiceId } = req.params; const { id: invoiceId } = req.params;
try { try {
const data = await this.saleInvoiceApplication.getSaleInvoiceMailState( const data = await this.saleInvoiceApplication.getSaleInvoiceMail(
tenantId, tenantId,
invoiceId invoiceId
); );

View File

@@ -11,7 +11,7 @@ import {
import { ServiceError } from '@/exceptions'; import { ServiceError } from '@/exceptions';
import DynamicListingService from '@/services/DynamicListing/DynamicListService'; import DynamicListingService from '@/services/DynamicListing/DynamicListService';
import CheckPolicies from '@/api/middleware/CheckPolicies'; import CheckPolicies from '@/api/middleware/CheckPolicies';
import { AbilitySubject, DiscountType, SaleReceiptAction } from '@/interfaces'; import { AbilitySubject, SaleReceiptAction } from '@/interfaces';
import { SaleReceiptApplication } from '@/services/Sales/Receipts/SaleReceiptApplication'; import { SaleReceiptApplication } from '@/services/Sales/Receipts/SaleReceiptApplication';
import { ACCEPT_TYPE } from '@/interfaces/Http'; import { ACCEPT_TYPE } from '@/interfaces/Http';
@@ -56,18 +56,8 @@ export default class SalesReceiptsController extends BaseController {
[ [
...this.specificReceiptValidationSchema, ...this.specificReceiptValidationSchema,
body('subject').isString().optional(), body('subject').isString().optional(),
body('from').isString().optional(), body('from').isString().optional(),
body('to').isString().optional(),
body('to').isArray().exists(),
body('to.*').isString().isEmail().optional(),
body('cc').isArray().optional({ nullable: true }),
body('cc.*').isString().isEmail().optional(),
body('bcc').isArray().optional({ nullable: true }),
body('bcc.*').isString().isEmail().optional(),
body('body').isString().optional(), body('body').isString().optional(),
body('attach_receipt').optional().isBoolean().toBoolean(), body('attach_receipt').optional().isBoolean().toBoolean(),
], ],
@@ -118,12 +108,6 @@ export default class SalesReceiptsController extends BaseController {
this.handleServiceErrors, this.handleServiceErrors,
this.dynamicListService.handlerErrorsToResponse this.dynamicListService.handlerErrorsToResponse
); );
router.get(
'/state',
CheckPolicies(SaleReceiptAction.View, AbilitySubject.SaleReceipt),
asyncMiddleware(this.getSaleReceiptState.bind(this)),
this.handleServiceErrors
);
router.get( router.get(
'/:id', '/:id',
CheckPolicies(SaleReceiptAction.View, AbilitySubject.SaleReceipt), CheckPolicies(SaleReceiptAction.View, AbilitySubject.SaleReceipt),
@@ -158,41 +142,23 @@ export default class SalesReceiptsController extends BaseController {
check('entries.*.id').optional({ nullable: true }).isNumeric().toInt(), check('entries.*.id').optional({ nullable: true }).isNumeric().toInt(),
check('entries.*.index').exists().isNumeric().toInt(), check('entries.*.index').exists().isNumeric().toInt(),
check('entries.*.item_id').exists().isNumeric().toInt(), check('entries.*.item_id').exists().isNumeric().toInt(),
check('entries.*.quantity').exists().isNumeric().toFloat(), check('entries.*.quantity').exists().isNumeric().toInt(),
check('entries.*.rate').exists().isNumeric().toFloat(), check('entries.*.rate').exists().isNumeric().toFloat(),
check('entries.*.discount') check('entries.*.discount')
.optional({ nullable: true }) .optional({ nullable: true })
.isNumeric() .isNumeric()
.toInt(), .toInt(),
check('entries.*.discount_type') check('entries.*.description')
.default(DiscountType.Percentage) .optional({ nullable: true })
.isString() .trim(),
.isIn([DiscountType.Percentage, DiscountType.Amount]),
check('entries.*.description').optional({ nullable: true }).trim(),
check('entries.*.warehouse_id') check('entries.*.warehouse_id')
.optional({ nullable: true }) .optional({ nullable: true })
.isNumeric() .isNumeric()
.toInt(), .toInt(),
check('receipt_message').optional().trim(), check('receipt_message').optional().trim(),
check('statement').optional().trim(), check('statement').optional().trim(),
check('attachments').isArray().optional(), check('attachments').isArray().optional(),
check('attachments.*.key').exists().isString(), check('attachments.*.key').exists().isString(),
// # Pdf template
check('pdf_template_id').optional({ nullable: true }).isNumeric().toInt(),
// # Discount
check('discount').optional({ nullable: true }).isNumeric().toFloat(),
check('discount_type')
.optional({ nullable: true })
.isString()
.isIn([DiscountType.Percentage, DiscountType.Amount]),
// # Adjustment
check('adjustment').optional({ nullable: true }).isNumeric().toFloat(),
]; ];
} }
@@ -378,28 +344,19 @@ export default class SalesReceiptsController extends BaseController {
const acceptType = accept.types([ const acceptType = accept.types([
ACCEPT_TYPE.APPLICATION_JSON, ACCEPT_TYPE.APPLICATION_JSON,
ACCEPT_TYPE.APPLICATION_PDF, ACCEPT_TYPE.APPLICATION_PDF,
ACCEPT_TYPE.APPLICATION_TEXT_HTML,
]); ]);
// Retrieves receipt in pdf format. // Retrieves receipt in pdf format.
if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) { if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) {
const [pdfContent, filename] = const pdfContent = await this.saleReceiptsApplication.getSaleReceiptPdf(
await this.saleReceiptsApplication.getSaleReceiptPdf(
tenantId,
saleReceiptId
);
res.set({
'Content-Type': 'application/pdf',
'Content-Length': pdfContent.length,
'Content-Disposition': `attachment; filename="${filename}"`,
});
res.send(pdfContent);
// Retrieves receipt in json format.
} else if (ACCEPT_TYPE.APPLICATION_TEXT_HTML === acceptType) {
const htmlContent = await this.saleReceiptsApplication.getSaleReceiptHtml(
tenantId, tenantId,
saleReceiptId saleReceiptId
); );
res.send({ htmlContent }); res.set({
'Content-Type': 'application/pdf',
'Content-Length': pdfContent.length,
});
res.send(pdfContent);
// Retrieves receipt in json format.
} else { } else {
const saleReceipt = await this.saleReceiptsApplication.getSaleReceipt( const saleReceipt = await this.saleReceiptsApplication.getSaleReceipt(
tenantId, tenantId,
@@ -409,30 +366,6 @@ export default class SalesReceiptsController extends BaseController {
} }
} }
/**
*
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
public async getSaleReceiptState(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
// Retrieves receipt in pdf format.
try {
const data = await this.saleReceiptsApplication.getSaleReceiptState(
tenantId
);
return res.status(200).send({ data });
} catch (error) {
next(error);
}
}
/** /**
* Sale receipt notification via SMS. * Sale receipt notification via SMS.
* @param {Request} req * @param {Request} req
@@ -539,7 +472,7 @@ export default class SalesReceiptsController extends BaseController {
const { id: receiptId } = req.params; const { id: receiptId } = req.params;
try { try {
const data = await this.saleReceiptsApplication.getSaleReceiptMailState( const data = await this.saleReceiptsApplication.getSaleReceiptMail(
tenantId, tenantId,
receiptId receiptId
); );

View File

@@ -1,118 +0,0 @@
import { Inject, Service } from 'typedi';
import { Router, Request, Response, NextFunction } from 'express';
import { param } from 'express-validator';
import BaseController from '@/api/controllers/BaseController';
import { PaymentLinksApplication } from '@/services/PaymentLinks/PaymentLinksApplication';
@Service()
export class PublicSharableLinkController extends BaseController {
@Inject()
private paymentLinkApp: PaymentLinksApplication;
/**
* Router constructor.
*/
router() {
const router = Router();
router.get(
'/:paymentLinkId/invoice',
[param('paymentLinkId').exists()],
this.validationResult,
this.getPaymentLinkPublicMeta.bind(this),
this.validationResult
);
router.get(
'/:paymentLinkId/invoice/pdf',
[param('paymentLinkId').exists()],
this.validationResult,
this.getPaymentLinkInvoicePdf.bind(this),
this.validationResult
);
router.post(
'/:paymentLinkId/stripe_checkout_session',
[param('paymentLinkId').exists()],
this.validationResult,
this.createInvoicePaymentLinkCheckoutSession.bind(this)
);
return router;
}
/**
* Retrieves the payment link public meta.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns
*/
public async getPaymentLinkPublicMeta(
req: Request<{ paymentLinkId: string }>,
res: Response,
next: NextFunction
) {
const { paymentLinkId } = req.params;
try {
const data = await this.paymentLinkApp.getInvoicePaymentLink(
paymentLinkId
);
return res.status(200).send({ data });
} catch (error) {
next(error);
}
}
/**
* Creates a Stripe checkout session for the given payment link id.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Promise<Response|void>}
*/
public async createInvoicePaymentLinkCheckoutSession(
req: Request<{ paymentLinkId: string }>,
res: Response,
next: NextFunction
) {
const { paymentLinkId } = req.params;
try {
const session =
await this.paymentLinkApp.createInvoicePaymentCheckoutSession(
paymentLinkId
);
return res.status(200).send(session);
} catch (error) {
next(error);
}
}
/**
* Retrieves the sale invoice pdf of the given payment link.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
public async getPaymentLinkInvoicePdf(
req: Request<{ paymentLinkId: string }>,
res: Response,
next: NextFunction
) {
const { paymentLinkId } = req.params;
try {
const [pdfContent, filename] =
await this.paymentLinkApp.getPaymentLinkInvoicePdf(paymentLinkId);
res.set({
'Content-Type': 'application/pdf',
'Content-Length': pdfContent.length,
'Content-Disposition': `attachment; filename="${filename}"`,
});
res.send(pdfContent);
} catch (error) {
next(error);
}
}
}

View File

@@ -1,63 +0,0 @@
import { Inject, Service } from 'typedi';
import { Router, Request, Response, NextFunction } from 'express';
import { body } from 'express-validator';
import { AbilitySubject, PaymentReceiveAction } from '@/interfaces';
import BaseController from '@/api/controllers/BaseController';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import CheckPolicies from '@/api/middleware/CheckPolicies';
import { GenerateShareLink } from '@/services/Sales/Invoices/GenerateeInvoicePaymentLink';
@Service()
export class ShareLinkController extends BaseController {
@Inject()
private generateShareLinkService: GenerateShareLink;
/**
* Router constructor.
*/
router() {
const router = Router();
router.post(
'/payment-links/generate',
CheckPolicies(PaymentReceiveAction.Edit, AbilitySubject.PaymentReceive),
[
body('transaction_type').exists(),
body('transaction_id').exists().isNumeric().toInt(),
body('publicity').optional(),
body('expiry_date').optional({ nullable: true }),
],
this.validationResult,
asyncMiddleware(this.generateShareLink.bind(this))
);
return router;
}
/**
* Generates sharable link for the given transaction.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
public async generateShareLink(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const { transactionType, transactionId, publicity, expiryDate } =
this.matchedBodyData(req);
try {
const link = await this.generateShareLinkService.generatePaymentLink(
tenantId,
transactionId,
publicity,
expiryDate
);
res.status(200).json({ link });
} catch (error) {
next(error);
}
}
}

View File

@@ -1,122 +0,0 @@
import { NextFunction, Request, Response, Router } from 'express';
import { body } from 'express-validator';
import { Service, Inject } from 'typedi';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import { StripePaymentApplication } from '@/services/StripePayment/StripePaymentApplication';
import BaseController from '../BaseController';
@Service()
export class StripeIntegrationController extends BaseController {
@Inject()
private stripePaymentApp: StripePaymentApplication;
public router() {
const router = Router();
router.get('/link', this.getStripeConnectLink.bind(this));
router.post(
'/callback',
[body('code').exists()],
this.validationResult,
this.exchangeOAuth.bind(this)
);
router.post('/account', asyncMiddleware(this.createAccount.bind(this)));
router.post(
'/account_link',
[body('stripe_account_id').exists()],
this.validationResult,
asyncMiddleware(this.createAccountLink.bind(this))
);
return router;
}
/**
* Retrieves Stripe OAuth2 connect link.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Promise<Response|void>}
*/
public async getStripeConnectLink(
req: Request,
res: Response,
next: NextFunction
) {
try {
const authorizationUri = this.stripePaymentApp.getStripeConnectLink();
return res.status(200).send({ url: authorizationUri });
} catch (error) {
next(error);
}
}
/**
* Exchanges the given Stripe authorization code to Stripe user id and access token.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Promise<void>}
*/
public async exchangeOAuth(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
const { code } = this.matchedBodyData(req);
try {
await this.stripePaymentApp.exchangeStripeOAuthToken(tenantId, code);
return res.status(200).send({});
} catch (error) {
next(error);
}
}
/**
* Creates a new Stripe account.
* @param {Request} req - The Express request object.
* @param {Response} res - The Express response object.
* @param {NextFunction} next - The Express next middleware function.
* @returns {Promise<void>}
*/
public async createAccount(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req;
try {
const accountId = await this.stripePaymentApp.createStripeAccount(
tenantId
);
return res.status(201).json({
accountId,
message: 'The Stripe account has been created successfully.',
});
} catch (error) {
next(error);
}
}
/**
* Creates a new Stripe account session.
* @param {Request} req - The Express request object.
* @param {Response} res - The Express response object.
* @param {NextFunction} next - The Express next middleware function.
* @returns {Promise<void>}
*/
public async createAccountLink(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const { stripeAccountId } = this.matchedBodyData(req);
try {
const clientSecret = await this.stripePaymentApp.createAccountLink(
tenantId,
stripeAccountId
);
return res.status(200).json({ clientSecret });
} catch (error) {
next(error);
}
}
}

View File

@@ -1,83 +0,0 @@
import { NextFunction, Request, Response, Router } from 'express';
import { Inject, Service } from 'typedi';
import bodyParser from 'body-parser';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import { StripeWebhookEventPayload } from '@/interfaces/StripePayment';
import { StripePaymentService } from '@/services/StripePayment/StripePaymentService';
import events from '@/subscribers/events';
import config from '@/config';
@Service()
export class StripeWebhooksController {
@Inject()
private stripePaymentService: StripePaymentService;
@Inject()
private eventPublisher: EventPublisher;
public router() {
const router = Router();
router.post(
'/stripe',
bodyParser.raw({ type: 'application/json' }),
this.handleWebhook.bind(this)
);
return router;
}
/**
* Handles incoming Stripe webhook events.
* Verifies the webhook signature, processes the event based on its type,
* and triggers appropriate actions or events in the system.
*
* @param {Request} req - The Express request object containing the webhook payload.
* @param {Response} res - The Express response object.
* @param {NextFunction} next - The Express next middleware function.
*/
private async handleWebhook(req: Request, res: Response, next: NextFunction) {
try {
let event = req.body;
const sig = req.headers['stripe-signature'];
// Verify webhook signature and extract the event.
// See https://stripe.com/docs/webhooks#verify-events for more information.
try {
event = this.stripePaymentService.stripe.webhooks.constructEvent(
req.rawBody,
sig,
config.stripePayment.webhooksSecret
);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the event based on its type
switch (event.type) {
case 'checkout.session.completed':
// Triggers `onStripeCheckoutSessionCompleted` event.
this.eventPublisher.emitAsync(
events.stripeWebhooks.onCheckoutSessionCompleted,
{
event,
} as StripeWebhookEventPayload
);
break;
case 'account.updated':
this.eventPublisher.emitAsync(
events.stripeWebhooks.onAccountUpdated,
{
event,
} as StripeWebhookEventPayload
);
break;
// Add more cases as needed
default:
console.log(`Unhandled event type ${event.type}`);
}
res.status(200).json({ received: true });
} catch (error) {
next(error);
}
}
}

View File

@@ -1,10 +1,9 @@
import { NextFunction, Router, Request, Response } from 'express'; import { NextFunction, Router, Request, Response } from 'express';
import Container, { Inject, Service } from 'typedi'; import { Inject, Service } from 'typedi';
import { PlaidApplication } from '@/services/Banking/Plaid/PlaidApplication'; import { PlaidApplication } from '@/services/Banking/Plaid/PlaidApplication';
import BaseController from '../BaseController'; import BaseController from '../BaseController';
import { LemonSqueezyWebhooks } from '@/services/Subscription/LemonSqueezyWebhooks'; import { LemonSqueezyWebhooks } from '@/services/Subscription/LemonSqueezyWebhooks';
import { PlaidWebhookTenantBootMiddleware } from '@/services/Banking/Plaid/PlaidWebhookTenantBootMiddleware'; import { PlaidWebhookTenantBootMiddleware } from '@/services/Banking/Plaid/PlaidWebhookTenantBootMiddleware';
import { StripeWebhooksController } from '../StripeIntegration/StripeWebhooksController';
@Service() @Service()
export class Webhooks extends BaseController { export class Webhooks extends BaseController {
@@ -25,8 +24,6 @@ export class Webhooks extends BaseController {
router.post('/lemon', this.lemonWebhooks.bind(this)); router.post('/lemon', this.lemonWebhooks.bind(this));
router.use(Container.get(StripeWebhooksController).router());
return router; return router;
} }

View File

@@ -64,11 +64,6 @@ import { Webhooks } from './controllers/Webhooks/Webhooks';
import { ExportController } from './controllers/Export/ExportController'; import { ExportController } from './controllers/Export/ExportController';
import { AttachmentsController } from './controllers/Attachments/AttachmentsController'; import { AttachmentsController } from './controllers/Attachments/AttachmentsController';
import { OneClickDemoController } from './controllers/OneClickDemo/OneClickDemoController'; import { OneClickDemoController } from './controllers/OneClickDemo/OneClickDemoController';
import { StripeIntegrationController } from './controllers/StripeIntegration/StripeIntegrationController';
import { ShareLinkController } from './controllers/ShareLink/ShareLinkController';
import { PublicSharableLinkController } from './controllers/ShareLink/PublicSharableLinkController';
import { PdfTemplatesController } from './controllers/PdfTemplates/PdfTemplatesController';
import { PaymentServicesController } from './controllers/PaymentServices/PaymentServicesController';
export default () => { export default () => {
const app = Router(); const app = Router();
@@ -86,11 +81,7 @@ export default () => {
app.use('/jobs', Container.get(Jobs).router()); app.use('/jobs', Container.get(Jobs).router());
app.use('/account', Container.get(Account).router()); app.use('/account', Container.get(Account).router());
app.use('/webhooks', Container.get(Webhooks).router()); app.use('/webhooks', Container.get(Webhooks).router());
app.use('/demo', Container.get(OneClickDemoController).router()); app.use('/demo', Container.get(OneClickDemoController).router())
app.use(
'/payment-links',
Container.get(PublicSharableLinkController).router()
);
// - Dashboard routes. // - Dashboard routes.
// --------------------------- // ---------------------------
@@ -156,22 +147,10 @@ export default () => {
dashboard.use('/import', Container.get(ImportController).router()); dashboard.use('/import', Container.get(ImportController).router());
dashboard.use('/export', Container.get(ExportController).router()); dashboard.use('/export', Container.get(ExportController).router());
dashboard.use('/attachments', Container.get(AttachmentsController).router()); dashboard.use('/attachments', Container.get(AttachmentsController).router());
dashboard.use(
'/stripe_integration',
Container.get(StripeIntegrationController).router()
);
dashboard.use(
'/pdf-templates',
Container.get(PdfTemplatesController).router()
);
dashboard.use(
'/payment-services',
Container.get(PaymentServicesController).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());
dashboard.use('/', Container.get(WarehousesItemController).router()); dashboard.use('/', Container.get(WarehousesItemController).router());
dashboard.use('/', Container.get(ShareLinkController).router());
dashboard.use('/dashboard', Container.get(DashboardController).router()); dashboard.use('/dashboard', Container.get(DashboardController).router());
dashboard.use('/', Container.get(Miscellaneous).router()); dashboard.use('/', Container.get(Miscellaneous).router());

View File

@@ -50,8 +50,7 @@ export const injectI18nUtils = (req) => {
export const initalizeTenantServices = async (tenantId: number) => { export const initalizeTenantServices = async (tenantId: number) => {
const tenant = await Tenant.query() const tenant = await Tenant.query()
.findById(tenantId) .findById(tenantId)
.withGraphFetched('metadata') .withGraphFetched('metadata');
.throwIfNotFound();
const tenantServices = Container.get(TenancyService); const tenantServices = Container.get(TenancyService);
const tenantsManager = Container.get(TenantsManagerService); const tenantsManager = Container.get(TenantsManagerService);

View File

@@ -259,17 +259,6 @@ module.exports = {
*/ */
posthog: { posthog: {
apiKey: process.env.POSTHOG_API_KEY, apiKey: process.env.POSTHOG_API_KEY,
host: process.env.POSTHOG_HOST, host: process.env.POSTHOG_HOST
}, }
/**
* Stripe Payment Integration.
*/
stripePayment: {
secretKey: process.env.STRIPE_PAYMENT_SECRET_KEY || '',
publishableKey: process.env.STRIPE_PAYMENT_PUBLISHABLE_KEY || '',
clientId: process.env.STRIPE_PAYMENT_CLIENT_ID || '',
redirectTo: process.env.STRIPE_PAYMENT_REDIRECT_URL || '',
webhooksSecret: process.env.STRIPE_PAYMENT_WEBHOOKS_SECRET || '',
},
}; };

View File

@@ -1,31 +1,15 @@
export const SALE_INVOICE_CREATED = 'Sale invoice created'; export const SALE_INVOICE_CREATED = 'Sale invoice created';
export const SALE_INVOICE_EDITED = 'Sale invoice edited'; export const SALE_INVOICE_EDITED = 'Sale invoice d';
export const SALE_INVOICE_DELETED = 'Sale invoice deleted'; export const SALE_INVOICE_DELETED = 'Sale invoice deleted';
export const SALE_INVOICE_MAIL_DELIVERED = 'Sale invoice mail delivered'; export const SALE_INVOICE_MAIL_DELIVERED = 'Sale invoice mail delivered';
export const SALE_INVOICE_VIEWED = 'Sale invoice viewed';
export const SALE_INVOICE_PDF_VIEWED = 'Sale invoice PDF viewed';
export const SALE_INVOICE_MAIL_SENT = 'Sale invoice mail sent';
export const SALE_INVOICE_MAIL_REMINDER_SENT =
'Sale invoice reminder mail sent';
export const SALE_ESTIMATE_CREATED = 'Sale estimate created'; export const SALE_ESTIMATE_CREATED = 'Sale estimate created';
export const SALE_ESTIMATE_EDITED = 'Sale estimate edited'; export const SALE_ESTIMATE_EDITED = 'Sale estimate edited';
export const SALE_ESTIMATE_DELETED = 'Sale estimate deleted'; export const SALE_ESTIMATE_DELETED = 'Sale estimate deleted';
export const SALE_ESTIMATE_PDF_VIEWED = 'Sale estimate PDF viewed';
export const SALE_ESTIMATE_VIEWED = 'Sale estimate viewed';
export const SALE_ESTIMATE_MAIL_SENT = 'Sale estimate mail sent';
export const PAYMENT_RECEIVED_CREATED = 'Payment received created'; export const PAYMENT_RECEIVED_CREATED = 'Payment received created';
export const PAYMENT_RECEIVED_EDITED = 'payment received edited'; export const PAYMENT_RECEIVED_EDITED = 'payment received edited';
export const PAYMENT_RECEIVED_DELETED = 'Payment received deleted'; export const PAYMENT_RECEIVED_DELETED = 'Payment received deleted';
export const PAYMENT_RECEIVED_PDF_VIEWED = 'Payment received PDF viewed';
export const PAYMENT_RECEIVED_MAIL_SENT = 'Payment received mail sent';
export const SALE_RECEIPT_PDF_VIEWED = 'Sale credit PDF viewed';
export const SALE_RECEIPT_MAIL_SENT = 'Sale credit mail sent';
export const CREDIT_NOTE_PDF_VIEWED = 'Credit note PDF viewed';
export const CREDIT_NOTE_MAIL_SENT = 'Credit note mail sent';
export const BILL_CREATED = 'Bill created'; export const BILL_CREATED = 'Bill created';
export const BILL_EDITED = 'Bill edited'; export const BILL_EDITED = 'Bill edited';
@@ -42,12 +26,10 @@ export const EXPENSE_DELETED = 'Expense deleted';
export const ACCOUNT_CREATED = 'Account created'; export const ACCOUNT_CREATED = 'Account created';
export const ACCOUNT_EDITED = 'Account Edited'; export const ACCOUNT_EDITED = 'Account Edited';
export const ACCOUNT_DELETED = 'Account deleted'; export const ACCOUNT_DELETED = 'Account deleted';
export const ACCOUNT_VIEWED = 'Account viewed';
export const ITEM_EVENT_CREATED = 'Item created'; export const ITEM_EVENT_CREATED = 'Item created';
export const ITEM_EVENT_EDITED = 'Item edited'; export const ITEM_EVENT_EDITED = 'Item edited';
export const ITEM_EVENT_DELETED = 'Item deleted'; export const ITEM_EVENT_DELETED = 'Item deleted';
export const ITEM_EVENT_VIEWED = 'Item viewed';
export const AUTH_SIGNED_UP = 'Auth Signed-up'; export const AUTH_SIGNED_UP = 'Auth Signed-up';
export const AUTH_RESET_PASSWORD = 'Auth reset password'; export const AUTH_RESET_PASSWORD = 'Auth reset password';
@@ -55,8 +37,6 @@ export const AUTH_RESET_PASSWORD = 'Auth reset password';
export const SUBSCRIPTION_CANCELLED = 'Subscription cancelled'; export const SUBSCRIPTION_CANCELLED = 'Subscription cancelled';
export const SUBSCRIPTION_RESUMED = 'Subscription resumed'; export const SUBSCRIPTION_RESUMED = 'Subscription resumed';
export const SUBSCRIPTION_PLAN_CHANGED = 'Subscription plan changed'; export const SUBSCRIPTION_PLAN_CHANGED = 'Subscription plan changed';
export const SUBSCRIPTION_PAYMENT_SUCCEED = 'Subscription payment succeed';
export const SUBSCRIPTION_PAYMENT_FAILED = 'Subscription payment failed';
export const CUSTOMER_CREATED = 'Customer created'; export const CUSTOMER_CREATED = 'Customer created';
export const CUSTOMER_EDITED = 'Customer edited'; export const CUSTOMER_EDITED = 'Customer edited';
@@ -89,19 +69,6 @@ export const BANK_RULE_CREATED = 'Bank rule created';
export const BANK_RULE_EDITED = 'Bank rule edited'; export const BANK_RULE_EDITED = 'Bank rule edited';
export const BANK_RULE_DELETED = 'Bank rule deleted'; export const BANK_RULE_DELETED = 'Bank rule deleted';
export const PDF_TEMPLATE_CREATED = 'PDF template created';
export const PDF_TEMPLATE_EDITED = 'PDF template edited';
export const PDF_TEMPLATE_DELETED = 'PDF template deleted';
export const PDF_TEMPLATE_ASSIGNED_DEFAULT = 'PDF template assigned as default';
export const PAYMENT_METHOD_EDITED = 'Payment method edited';
export const PAYMENT_METHOD_DELETED = 'Payment method deleted';
export const INVOICE_PAYMENT_LINK_GENERATED = 'Invoice payment link generated';
export const STRIPE_INTEGRAION_CONNECTED =
'Stripe integration oauth2 connected';
// # Event Groups // # Event Groups
export const ACCOUNT_GROUP = 'Account'; export const ACCOUNT_GROUP = 'Account';
export const ITEM_GROUP = 'Item'; export const ITEM_GROUP = 'Item';
@@ -110,21 +77,3 @@ export const SALE_GROUP = 'Sale';
export const PAYMENT_GROUP = 'Payment'; export const PAYMENT_GROUP = 'Payment';
export const BILL_GROUP = 'Bill'; export const BILL_GROUP = 'Bill';
export const EXPENSE_GROUP = 'Expense'; export const EXPENSE_GROUP = 'Expense';
// # Reports
export const BALANCE_SHEET_VIEWED = 'Balance sheet viewed';
export const TRIAL_BALANCE_SHEET_VIEWED = 'Trial balance sheet viewed';
export const PROFIT_LOSS_SHEET_VIEWED = 'Profit loss sheet viewed';
export const CASHFLOW_STATEMENT_VIEWED = 'Cashflow statement viewed';
export const GENERAL_LEDGER_VIEWED = 'General ledger viewed';
export const JOURNAL_VIEWED = 'Journal viewed';
export const RECEIVABLE_AGING_VIEWED = 'Receivable aging viewed';
export const PAYABLE_AGING_VIEWED = 'Payable aging viewed';
export const CUSTOMER_BALANCE_SUMMARY_VIEWED =
'Customer balance summary viewed';
export const VENDOR_BALANCE_SUMMARY_VIEWED = 'Vendor balance summary viewed';
export const INVENTORY_VALUATION_VIEWED = 'Inventory valuation viewed';
export const CUSTOMER_TRANSACTIONS_VIEWED = 'Customer transactions viewed';
export const VENDOR_TRANSACTIONS_VIEWED = 'Vendor transactions viewed';
export const SALES_BY_ITEM_VIEWED = 'Sales by item viewed';
export const PURCHASES_BY_ITEM_VIEWED = 'Purchases by item viewed';

View File

@@ -129,7 +129,6 @@ export const ACCOUNT_TYPES = [
normal: ACCOUNT_NORMAL.CREDIT, normal: ACCOUNT_NORMAL.CREDIT,
rootType: ACCOUNT_ROOT_TYPE.LIABILITY, rootType: ACCOUNT_ROOT_TYPE.LIABILITY,
parentType: ACCOUNT_PARENT_TYPE.CURRENT_LIABILITY, parentType: ACCOUNT_PARENT_TYPE.CURRENT_LIABILITY,
multiCurrency: true,
balanceSheet: true, balanceSheet: true,
incomeSheet: false, incomeSheet: false,
}, },

View File

@@ -1,19 +0,0 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema.table('payment_receives', (table) => {
table.string('stripe_pintent_id').nullable();
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema.table('payment_receives', (table) => {
table.dropColumn('stripe_pintent_id');
});
};

View File

@@ -1,75 +0,0 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema
.createTable('pdf_templates', (table) => {
table.increments('id').primary();
table.text('resource');
table.text('template_name');
table.json('attributes');
table.boolean('predefined').defaultTo(false);
table.boolean('default').defaultTo(false);
table.timestamps();
})
.table('sales_invoices', (table) => {
table
.integer('pdf_template_id')
.unsigned()
.references('id')
.inTable('pdf_templates');
})
.table('sales_estimates', (table) => {
table
.integer('pdf_template_id')
.unsigned()
.references('id')
.inTable('pdf_templates');
})
.table('sales_receipts', (table) => {
table
.integer('pdf_template_id')
.unsigned()
.references('id')
.inTable('pdf_templates');
})
.table('credit_notes', (table) => {
table
.integer('pdf_template_id')
.unsigned()
.references('id')
.inTable('pdf_templates');
})
.table('payment_receives', (table) => {
table
.integer('pdf_template_id')
.unsigned()
.references('id')
.inTable('pdf_templates');
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema
.table('payment_receives', (table) => {
table.dropColumn('pdf_template_id');
})
.table('credit_notes', (table) => {
table.dropColumn('pdf_template_id');
})
.table('sales_receipts', (table) => {
table.dropColumn('pdf_template_id');
})
.table('sales_estimates', (table) => {
table.dropColumn('pdf_template_id');
})
.table('sales_invoices', (table) => {
table.dropColumn('pdf_template_id');
})
.dropTableIfExists('pdf_templates');
};

View File

@@ -1,25 +0,0 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema.createTable('payment_integrations', (table) => {
table.increments('id');
table.string('service');
table.string('name');
table.string('slug');
table.boolean('payment_enabled').defaultTo(false);
table.boolean('payout_enabled').defaultTo(false);
table.string('account_id');
table.json('options');
table.timestamps();
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema.dropTableIfExists('payment_integrations');
};

View File

@@ -1,27 +0,0 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema.createTable('transactions_payment_methods', (table) => {
table.increments('id');
table.integer('reference_id').unsigned();
table.string('reference_type');
table
.integer('payment_integration_id')
.unsigned()
.index()
.references('id')
.inTable('payment_integrations');
table.boolean('enable').defaultTo(false);
table.json('options').nullable();
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema.dropTableIfExists('transactions_payment_methods');
};

View File

@@ -1,44 +0,0 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex('pdf_templates').insert([
{
resource: 'SaleInvoice',
templateName: 'Standard Template',
predefined: true,
default: true,
},
{
resource: 'SaleEstimate',
templateName: 'Standard Template',
predefined: true,
default: true,
},
{
resource: 'SaleReceipt',
templateName: 'Standard Template',
predefined: true,
default: true,
},
{
resource: 'CreditNote',
templateName: 'Standard Template',
predefined: true,
default: true,
},
{
resource: 'PaymentReceive',
templateName: 'Standard Template',
predefined: true,
default: true,
},
]);
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {};

View File

@@ -1,39 +0,0 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema
.table('items_entries', (table) => {
table.decimal('quantity', 13, 3).alter();
})
.table('inventory_transactions', (table) => {
table.decimal('quantity', 13, 3).alter();
})
.table('inventory_cost_lot_tracker', (table) => {
table.decimal('quantity', 13, 3).alter();
})
.table('items', (table) => {
table.decimal('quantityOnHand', 13, 3).alter();
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema
.table('items_entries', (table) => {
table.integer('quantity').alter();
})
.table('inventory_transactions', (table) => {
table.integer('quantity').alter();
})
.table('inventory_cost_lot_tracker', (table) => {
table.integer('quantity').alter();
})
.table('items', (table) => {
table.integer('quantityOnHand').alter();
});
};

View File

@@ -1,23 +0,0 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema.alterTable('sales_invoices', (table) => {
table.decimal('discount', 10, 2).nullable().after('credited_amount');
table.string('discount_type').nullable().after('discount');
table.decimal('adjustment', 10, 2).nullable().after('discount_type');
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema.alterTable('sale_invoices', (table) => {
table.dropColumn('discount');
table.dropColumn('discount_type');
table.dropColumn('adjustment');
});
};

View File

@@ -1,24 +0,0 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function(knex) {
return knex.schema.alterTable('sales_estimates', (table) => {
table.decimal('discount', 10, 2).nullable().after('amount');
table.string('discount_type').nullable().after('discount');
table.decimal('adjustment', 10, 2).nullable().after('discount_type');
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function(knex) {
return knex.schema.alterTable('sales_estimates', (table) => {
table.dropColumn('discount');
table.dropColumn('discount_type');
table.dropColumn('adjustment');
});
};

View File

@@ -1,24 +0,0 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function(knex) {
return knex.schema.alterTable('sales_receipts', (table) => {
table.decimal('discount', 10, 2).nullable().after('amount');
table.string('discount_type').nullable().after('discount');
table.decimal('adjustment', 10, 2).nullable().after('discount_type');
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function(knex) {
return knex.schema.alterTable('sales_receipts', (table) => {
table.dropColumn('discount');
table.dropColumn('discount_type');
table.dropColumn('adjustment');
});
};

View File

@@ -1,26 +0,0 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function(knex) {
return knex.schema.alterTable('bills', (table) => {
// Discount.
table.decimal('discount', 10, 2).nullable().after('amount');
table.string('discount_type').nullable().after('discount');
// Adjustment.
table.decimal('adjustment', 10, 2).nullable().after('discount_type');
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function(knex) {
return knex.schema.alterTable('bills', (table) => {
table.dropColumn('discount');
table.dropColumn('discount_type');
table.dropColumn('adjustment');
});
};

View File

@@ -1,23 +0,0 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function(knex) {
return knex.schema.alterTable('credit_notes', (table) => {
table.decimal('discount', 10, 2).nullable().after('exchange_rate');
table.string('discount_type').nullable().after('discount');
table.decimal('adjustment', 10, 2).nullable().after('discount_type');
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function(knex) {
return knex.schema.alterTable('credit_notes', (table) => {
table.dropColumn('discount');
table.dropColumn('discount_type');
table.dropColumn('adjustment');
});
};

View File

@@ -1,23 +0,0 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function(knex) {
return knex.schema.alterTable('vendor_credits', (table) => {
table.decimal('discount', 10, 2).nullable().after('amount');
table.string('discount_type').nullable().after('discount');
table.decimal('adjustment', 10, 2).nullable().after('discount_type');
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function(knex) {
return knex.schema.alterTable('vendor_credits', (table) => {
table.dropColumn('discount');
table.dropColumn('discount_type');
table.dropColumn('adjustment');
});
};

View File

@@ -1,19 +0,0 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema.alterTable('items_entries', (table) => {
table.string('discount_type').defaultTo('percentage').after('discount');
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema.alterTable('items_entries', (table) => {
table.dropColumn('discount_type');
});
};

View File

@@ -1,14 +1,3 @@
export const OtherExpensesAccount = {
name: 'Other Expenses',
slug: 'other-expenses',
account_type: 'other-expense',
code: '40011',
description: '',
active: 1,
index: 1,
predefined: 1,
};
export const TaxPayableAccount = { export const TaxPayableAccount = {
name: 'Tax Payable', name: 'Tax Payable',
slug: 'tax-payable', slug: 'tax-payable',
@@ -42,47 +31,6 @@ export const PrepardExpenses = {
predefined: true, predefined: true,
}; };
export const StripeClearingAccount = {
name: 'Stripe Clearing',
slug: 'stripe-clearing',
account_type: 'other-current-asset',
parent_account_id: null,
code: '100020',
active: true,
index: 1,
predefined: true,
};
export const DiscountExpenseAccount = {
name: 'Discount',
slug: 'discount',
account_type: 'other-income',
code: '40008',
active: true,
index: 1,
predefined: true,
};
export const PurchaseDiscountAccount = {
name: 'Purchase Discount',
slug: 'purchase-discount',
account_type: 'other-expense',
code: '40009',
active: true,
index: 1,
predefined: true,
};
export const OtherChargesAccount = {
name: 'Other Charges',
slug: 'other-charges',
account_type: 'other-income',
code: '40010',
active: true,
index: 1,
predefined: true,
};
export default [ export default [
{ {
name: 'Bank Account', name: 'Bank Account',
@@ -272,7 +220,17 @@ export default [
}, },
// Expenses // Expenses
OtherExpensesAccount, {
name: 'Other Expenses',
slug: 'other-expenses',
account_type: 'other-expense',
parent_account_id: null,
code: '40001',
description: '',
active: 1,
index: 1,
predefined: 1,
},
{ {
name: 'Cost of Goods Sold', name: 'Cost of Goods Sold',
slug: 'cost-of-goods-sold', slug: 'cost-of-goods-sold',
@@ -389,7 +347,4 @@ export default [
}, },
UnearnedRevenueAccount, UnearnedRevenueAccount,
PrepardExpenses, PrepardExpenses,
DiscountExpenseAccount,
PurchaseDiscountAccount,
OtherChargesAccount,
]; ];

View File

@@ -0,0 +1,63 @@
export const swaggerDocs = {
tags: {},
paths: {},
};
// Decorator to set a tag for a route
export function ApiTags(tag) {
return function (target) {
if (!swaggerDocs.tags[tag]) {
swaggerDocs.tags[tag] = { name: tag };
}
};
}
// Decorator to add an operation for a specific route
export function ApiOperation(options) {
return function (target, propertyKey, descriptor) {
const routePath = Reflect.getMetadata('path', target, propertyKey);
swaggerDocs.paths[routePath] = swaggerDocs.paths[routePath] || {};
swaggerDocs.paths[routePath].get = {
summary: options.summary,
description: options.description || '',
responses: options.responses || {
200: {
description: 'Successful Response',
},
},
};
};
}
// Decorator to define the route path
export function Route(path) {
return function (target, propertyKey, descriptor) {
Reflect.defineMetadata('path', path, target, propertyKey);
};
}
// Decorator to add a response schema for a specific route
export function ApiResponse(options) {
return function (target, propertyKey, descriptor) {
const routePath = Reflect.getMetadata('path', target, propertyKey);
if (!swaggerDocs.paths[routePath]) {
swaggerDocs.paths[routePath] = { get: {} };
}
swaggerDocs.paths[routePath].get.responses =
swaggerDocs.paths[routePath].get.responses || {};
swaggerDocs.paths[routePath].get.responses[options.status] = {
description: options.description || 'No description provided',
content: {
'application/json': {
schema: options.schema || {},
},
},
};
};
}

View File

@@ -3,7 +3,6 @@ import { IDynamicListFilterDTO } from './DynamicFilter';
import { IItemEntry, IItemEntryDTO } from './ItemEntry'; import { IItemEntry, IItemEntryDTO } from './ItemEntry';
import { IBillLandedCost } from './LandedCost'; import { IBillLandedCost } from './LandedCost';
import { AttachmentLinkDTO } from './Attachments'; import { AttachmentLinkDTO } from './Attachments';
import { DiscountType } from './SaleInvoice';
export interface IBillDTO { export interface IBillDTO {
vendorId: number; vendorId: number;
@@ -23,13 +22,6 @@ export interface IBillDTO {
projectId?: number; projectId?: number;
isInclusiveTax?: boolean; isInclusiveTax?: boolean;
attachments?: AttachmentLinkDTO[]; attachments?: AttachmentLinkDTO[];
// # Discount
discount?: number;
discountType?: DiscountType;
// # Adjustment
adjustment?: number;
} }
export interface IBillEditDTO { export interface IBillEditDTO {

View File

@@ -146,7 +146,6 @@ export interface ICashflowTransactionUncategorizedPayload {
tenantId: number; tenantId: number;
uncategorizedTransactionId: number; uncategorizedTransactionId: number;
uncategorizedTransactions: Array<IUncategorizedCashflowTransaction>; uncategorizedTransactions: Array<IUncategorizedCashflowTransaction>;
oldMainUncategorizedTransaction: IUncategorizedCashflowTransaction;
oldUncategorizedTransactions: Array<IUncategorizedCashflowTransaction>; oldUncategorizedTransactions: Array<IUncategorizedCashflowTransaction>;
trx: Knex.Transaction; trx: Knex.Transaction;
} }

View File

@@ -1,5 +1,5 @@
import { Knex } from 'knex'; import { Knex } from 'knex';
import { DiscountType, IDynamicListFilter, IItemEntry } from '@/interfaces'; import { IDynamicListFilter, IItemEntry } from '@/interfaces';
import { ILedgerEntry } from './Ledger'; import { ILedgerEntry } from './Ledger';
import { AttachmentLinkDTO } from './Attachments'; import { AttachmentLinkDTO } from './Attachments';
@@ -23,9 +23,6 @@ export interface ICreditNoteNewDTO {
branchId?: number; branchId?: number;
warehouseId?: number; warehouseId?: number;
attachments?: AttachmentLinkDTO[]; attachments?: AttachmentLinkDTO[];
discount?: number;
discountType?: DiscountType;
adjustment?: number;
} }
export interface ICreditNoteEditDTO { export interface ICreditNoteEditDTO {
@@ -65,8 +62,6 @@ export interface ICreditNote {
branchId?: number; branchId?: number;
warehouseId: number; warehouseId: number;
createdAt?: Date; createdAt?: Date;
termsConditions: string;
note: string;
} }
export enum CreditNoteAction { export enum CreditNoteAction {
@@ -263,61 +258,3 @@ export type ICreditNoteGLCommonEntry = Pick<
| 'debit' | 'debit'
| 'branchId' | 'branchId'
>; >;
export interface CreditNotePdfTemplateAttributes {
// # Primary color
primaryColor: string;
secondaryColor: string;
// # Company logo
showCompanyLogo: boolean;
companyLogo: string;
// # Company name
companyName: string;
// # Customer Address
showCustomerAddress: boolean;
customerAddress: string;
// # Company address
showCompanyAddress: boolean;
companyAddress: string;
billedToLabel: string;
total: string;
totalLabel: string;
showTotal: boolean;
subtotal: string;
subtotalLabel: string;
showSubtotal: boolean;
showCustomerNote: boolean;
customerNote: string;
customerNoteLabel: string;
showTermsConditions: boolean;
termsConditions: string;
termsConditionsLabel: string;
lines: Array<{
item: string;
description: string;
rate: string;
quantity: string;
total: string;
}>;
showCreditNoteNumber: boolean;
creditNoteNumberLabel: string;
creditNoteNumebr: string;
creditNoteDate: string;
showCreditNoteDate: boolean;
creditNoteDateLabel: string;
}
export interface ICreditNoteState {
defaultTemplateId: number;
}

View File

@@ -4,5 +4,4 @@ export const ACCEPT_TYPE = {
APPLICATION_JSON_TABLE: 'application/json+table', APPLICATION_JSON_TABLE: 'application/json+table',
APPLICATION_XLSX: 'application/xlsx', APPLICATION_XLSX: 'application/xlsx',
APPLICATION_CSV: 'application/csv', APPLICATION_CSV: 'application/csv',
APPLICATION_TEXT_HTML: 'application/json+html',
}; };

View File

@@ -13,17 +13,14 @@ export interface IItemEntry {
itemId: number; itemId: number;
description: string; description: string;
discountType?: string;
discount: number; discount: number;
quantity: number; quantity: number;
rate: number; rate: number;
amount: number; amount: number;
total: number; total: number;
totalExcludingTax?: number; amountInclusingTax: number;
amountExludingTax: number;
subtotalInclusingTax: number;
subtotalExcludingTax: number;
discountAmount: number; discountAmount: number;
landedCost: number; landedCost: number;

View File

@@ -30,15 +30,18 @@ export interface AddressItem {
} }
export interface CommonMailOptions { export interface CommonMailOptions {
from: Array<string>; toAddresses: AddressItem[];
fromAddresses: AddressItem[];
from: string;
to: string | string[];
subject: string; subject: string;
message: string; body: string;
to: Array<string>; data?: Record<string, any>;
cc?: Array<string>;
bcc?: Array<string>;
formatArgs?: Record<string, any>;
toOptions: Array<AddressItem>;
fromOptions: Array<AddressItem>;
} }
export interface CommonMailOptionsDTO extends Partial<CommonMailOptions> {} export interface CommonMailOptionsDTO {
to?: string | string[];
from?: string;
subject?: string;
body?: string;
}

View File

@@ -25,7 +25,6 @@ export interface IPaymentReceived {
updatedAt: Date; updatedAt: Date;
localAmount?: number; localAmount?: number;
branchId?: number; branchId?: number;
pdfTemplateId?: number;
} }
export interface IPaymentReceivedCreateDTO { export interface IPaymentReceivedCreateDTO {
customerId: number; customerId: number;
@@ -177,9 +176,7 @@ export type IPaymentReceiveGLCommonEntry = Pick<
| 'branchId' | 'branchId'
>; >;
export interface PaymentReceiveMailOpts extends CommonMailOptions { export interface PaymentReceiveMailOpts extends CommonMailOptions {}
attachPdf?: boolean;
}
export interface PaymentReceiveMailOptsDTO extends CommonMailOptionsDTO {} export interface PaymentReceiveMailOptsDTO extends CommonMailOptionsDTO {}
@@ -188,59 +185,3 @@ export interface PaymentReceiveMailPresendEvent {
paymentReceiveId: number; paymentReceiveId: number;
messageOptions: PaymentReceiveMailOptsDTO; messageOptions: PaymentReceiveMailOptsDTO;
} }
export interface PaymentReceivedPdfLineItem {
item: string;
description: string;
rate: string;
quantity: string;
total: string;
}
export interface PaymentReceivedPdfTax {
label: string;
amount: string;
}
export interface PaymentReceivedPdfTemplateAttributes {
primaryColor: string;
secondaryColor: string;
showCompanyLogo: boolean;
companyLogo: string;
companyName: string;
// Customer Address
showCustomerAddress: boolean;
customerAddress: string;
// Company address
showCompanyAddress: boolean;
companyAddress: string;
billedToLabel: string;
total: string;
totalLabel: string;
showTotal: boolean;
subtotal: string;
subtotalLabel: string;
showSubtotal: boolean;
lines: Array<{
invoiceNumber: string;
invoiceAmount: string;
paidAmount: string;
}>;
showPaymentReceivedNumber: boolean;
paymentReceivedNumberLabel: string;
paymentReceivedNumebr: string;
paymentReceivedDate: string;
showPaymentReceivedDate: boolean;
paymentReceivedDateLabel: string;
}
export interface IPaymentReceivedState {
defaultTemplateId: number;
}

View File

@@ -3,7 +3,6 @@ import { IItemEntry, IItemEntryDTO } from './ItemEntry';
import { IDynamicListFilterDTO } from '@/interfaces/DynamicFilter'; import { IDynamicListFilterDTO } from '@/interfaces/DynamicFilter';
import { CommonMailOptions, CommonMailOptionsDTO } from './Mailable'; import { CommonMailOptions, CommonMailOptionsDTO } from './Mailable';
import { AttachmentLinkDTO } from './Attachments'; import { AttachmentLinkDTO } from './Attachments';
import { DiscountType } from './SaleInvoice';
export interface ISaleEstimate { export interface ISaleEstimate {
id?: number; id?: number;
@@ -25,14 +24,6 @@ export interface ISaleEstimate {
branchId?: number; branchId?: number;
warehouseId?: number; warehouseId?: number;
total?: number;
totalLocal?: number;
discountAmount?: number;
discountPercentage?: number | null;
adjustment?: number;
} }
export interface ISaleEstimateDTO { export interface ISaleEstimateDTO {
customerId: number; customerId: number;
@@ -49,13 +40,6 @@ export interface ISaleEstimateDTO {
branchId?: number; branchId?: number;
warehouseId?: number; warehouseId?: number;
attachments?: AttachmentLinkDTO[]; attachments?: AttachmentLinkDTO[];
// # Discount
discount?: number;
discountType?: DiscountType;
// # Adjustment
adjustment?: number;
} }
export interface ISalesEstimatesFilter extends IDynamicListFilterDTO { export interface ISalesEstimatesFilter extends IDynamicListFilterDTO {
@@ -159,7 +143,3 @@ export interface ISaleEstimateMailPresendEvent {
saleEstimateId: number; saleEstimateId: number;
messageOptions: SaleEstimateMailOptionsDTO; messageOptions: SaleEstimateMailOptionsDTO;
} }
export interface ISaleEstimateState {
defaultTemplateId: number;
}

View File

@@ -5,34 +5,6 @@ import { IDynamicListFilter } from '@/interfaces/DynamicFilter';
import { IItemEntry, IItemEntryDTO } from './ItemEntry'; import { IItemEntry, IItemEntryDTO } from './ItemEntry';
import { AttachmentLinkDTO } from './Attachments'; import { AttachmentLinkDTO } from './Attachments';
export interface PaymentIntegrationTransactionLink {
id: number;
enable: true;
paymentIntegrationId: number;
referenceType: string;
referenceId: number;
}
export interface PaymentIntegrationTransactionLinkEventPayload {
tenantId: number;
enable: true;
paymentIntegrationId: number;
referenceType: string;
referenceId: number;
saleInvoiceId: number;
trx?: Knex.Transaction;
}
export interface PaymentIntegrationTransactionLinkDeleteEventPayload {
tenantId: number;
enable: true;
paymentIntegrationId: number;
referenceType: string;
referenceId: number;
oldSaleInvoiceId: number;
trx?: Knex.Transaction;
}
export interface ISaleInvoice { export interface ISaleInvoice {
id: number; id: number;
amount: number; amount: number;
@@ -73,25 +45,6 @@ export interface ISaleInvoice {
subtotal: number; subtotal: number;
subtotalLocal: number; subtotalLocal: number;
subtotalExludingTax: number; subtotalExludingTax: number;
termsConditions: string;
invoiceMessage: string;
pdfTemplateId?: number;
paymentMethods?: Array<PaymentIntegrationTransactionLink>;
adjustment?: number;
adjustmentLocal?: number | null;
discount?: number;
discountAmount?: number;
discountAmountLocal?: number | null;
}
export enum DiscountType {
Percentage = 'percentage',
Amount = 'amount',
} }
export interface ISaleInvoiceDTO { export interface ISaleInvoiceDTO {
@@ -114,13 +67,6 @@ export interface ISaleInvoiceDTO {
isInclusiveTax?: boolean; isInclusiveTax?: boolean;
attachments?: AttachmentLinkDTO[]; attachments?: AttachmentLinkDTO[];
// # Discount
discount?: number;
discountType?: DiscountType;
// # Adjustments
adjustments?: string;
} }
export interface ISaleInvoiceCreateDTO extends ISaleInvoiceDTO { export interface ISaleInvoiceCreateDTO extends ISaleInvoiceDTO {
@@ -185,13 +131,7 @@ export interface ISaleInvoiceEditingPayload {
export interface ISaleInvoiceDeletePayload { export interface ISaleInvoiceDeletePayload {
tenantId: number; tenantId: number;
oldSaleInvoice: ISaleInvoice; saleInvoice: ISaleInvoice;
saleInvoiceId: number;
}
export interface ISaleInvoiceDeletingPayload {
tenantId: number;
oldSaleInvoice: ISaleInvoice;
saleInvoiceId: number; saleInvoiceId: number;
trx: Knex.Transaction; trx: Knex.Transaction;
} }
@@ -253,32 +193,7 @@ export enum SaleInvoiceAction {
} }
export interface SaleInvoiceMailOptions extends CommonMailOptions { export interface SaleInvoiceMailOptions extends CommonMailOptions {
attachInvoice?: boolean; attachInvoice: boolean;
formatArgs?: Record<string, any>;
}
export interface SaleInvoiceMailState extends SaleInvoiceMailOptions {
invoiceNo: string;
invoiceDate: string;
invoiceDateFormatted: string;
dueDate: string;
dueDateFormatted: string;
total: number;
totalFormatted: string;
subtotal: number;
subtotalFormatted: number;
companyName: string;
companyLogoUri: string;
customerName: string;
// # Invoice entries
entries?: Array<{ label: string; total: string; quantity: string | number }>;
} }
export interface SendInvoiceMailDTO extends CommonMailOptionsDTO { export interface SendInvoiceMailDTO extends CommonMailOptionsDTO {
@@ -295,7 +210,6 @@ export interface ISaleInvoiceMailSend {
tenantId: number; tenantId: number;
saleInvoiceId: number; saleInvoiceId: number;
messageOptions: SendInvoiceMailDTO; messageOptions: SendInvoiceMailDTO;
formattedMessageOptions: SaleInvoiceMailOptions;
} }
export interface ISaleInvoiceMailSent { export interface ISaleInvoiceMailSent {
@@ -303,89 +217,3 @@ export interface ISaleInvoiceMailSent {
saleInvoiceId: number; saleInvoiceId: number;
messageOptions: SendInvoiceMailDTO; messageOptions: SendInvoiceMailDTO;
} }
// Invoice Pdf Document
export interface InvoicePdfLine {
item: string;
description: string;
rate: string;
quantity: string;
total: string;
}
export interface InvoicePdfTax {
label: string;
amount: string;
}
export interface InvoicePdfTemplateAttributes {
primaryColor: string;
secondaryColor: string;
companyName: string;
showCompanyLogo: boolean;
companyLogo: string;
dueDate: string;
dueDateLabel: string;
showDueDate: boolean;
dateIssue: string;
dateIssueLabel: string;
showDateIssue: boolean;
invoiceNumberLabel: string;
invoiceNumber: string;
showInvoiceNumber: boolean;
// Customer Address
showCustomerAddress: boolean;
customerAddress: string;
// Company address
showCompanyAddress: boolean;
companyAddress: string;
billedToLabel: string;
lineItemLabel: string;
lineDescriptionLabel: string;
lineRateLabel: string;
lineTotalLabel: string;
totalLabel: string;
subtotalLabel: string;
discountLabel: string;
paymentMadeLabel: string;
showTotal: boolean;
showSubtotal: boolean;
showDiscount: boolean;
showTaxes: boolean;
showPaymentMade: boolean;
total: string;
subtotal: string;
discount: string;
paymentMade: string;
// Due Amount
dueAmount: string;
showDueAmount: boolean;
dueAmountLabel: string;
termsConditionsLabel: string;
showTermsConditions: boolean;
termsConditions: string;
lines: InvoicePdfLine[];
taxes: InvoicePdfTax[];
statementLabel: string;
showStatement: boolean;
statement: string;
}
export interface ISaleInvocieState {
defaultTemplateId: number;
}

View File

@@ -2,7 +2,6 @@ import { Knex } from 'knex';
import { IItemEntry } from './ItemEntry'; import { IItemEntry } from './ItemEntry';
import { CommonMailOptions, CommonMailOptionsDTO } from './Mailable'; import { CommonMailOptions, CommonMailOptionsDTO } from './Mailable';
import { AttachmentLinkDTO } from './Attachments'; import { AttachmentLinkDTO } from './Attachments';
import { DiscountType } from './SaleInvoice';
export interface ISaleReceipt { export interface ISaleReceipt {
id?: number; id?: number;
@@ -28,20 +27,6 @@ export interface ISaleReceipt {
localAmount?: number; localAmount?: number;
entries?: IItemEntry[]; entries?: IItemEntry[];
subtotal?: number;
subtotalLocal?: number;
total?: number;
totalLocal?: number;
discountAmount: number;
discountPercentage?: number | null;
adjustment?: number;
adjustmentLocal?: number | null;
discountAmountLocal?: number | null;
} }
export interface ISalesReceiptsFilter { export interface ISalesReceiptsFilter {
@@ -62,11 +47,6 @@ export interface ISaleReceiptDTO {
entries: any[]; entries: any[];
branchId?: number; branchId?: number;
attachments?: AttachmentLinkDTO[]; attachments?: AttachmentLinkDTO[];
discount?: number;
discountType?: DiscountType;
adjustment?: number;
} }
export interface ISalesReceiptsService { export interface ISalesReceiptsService {
@@ -175,64 +155,3 @@ export interface ISaleReceiptMailPresend {
saleReceiptId: number; saleReceiptId: number;
messageOptions: SaleReceiptMailOptsDTO; messageOptions: SaleReceiptMailOptsDTO;
} }
export interface ISaleReceiptBrandingTemplateAttributes {
primaryColor: string;
secondaryColor: string;
showCompanyLogo: boolean;
companyLogo: string;
companyName: string;
// Customer Address
showCustomerAddress: boolean;
customerAddress: string;
// Company address
showCompanyAddress: boolean;
companyAddress: string;
billedToLabel: string;
// Total
total: string;
totalLabel: string;
showTotal: boolean;
// Subtotal
subtotal: string;
subtotalLabel: string;
showSubtotal: boolean;
// Customer Note
showCustomerNote: boolean;
customerNote: string;
customerNoteLabel: string;
// Terms & Conditions
showTermsConditions: boolean;
termsConditions: string;
termsConditionsLabel: string;
// Lines
lines: Array<{
item: string;
description: string;
rate: string;
quantity: string;
total: string;
}>;
// Receipt Number
showReceiptNumber: boolean;
receiptNumberLabel: string;
receiptNumebr: string;
// Receipt Date
receiptDate: string;
showReceiptDate: boolean;
receiptDateLabel: string;
}
export interface ISaleReceiptState {
defaultTemplateId: number;
}

View File

@@ -18,26 +18,14 @@ export interface IOrganizationBuildDTO {
dateFormat?: string; dateFormat?: string;
} }
interface OrganizationAddressDTO {
address1: string;
address2: string;
postalCode: string;
city: string;
stateProvince: string;
phone: string;
}
export interface IOrganizationUpdateDTO { export interface IOrganizationUpdateDTO {
name: string; name: string;
location?: string; location: string;
baseCurrency?: string; baseCurrency: string;
timezone?: string; timezone: string;
fiscalYear?: string; fiscalYear: string;
industry?: string; industry: string;
taxNumber?: string; taxNumber: string;
primaryColor?: string;
logoKey?: string;
address?: OrganizationAddressDTO;
} }
export interface IOrganizationBuildEventPayload { export interface IOrganizationBuildEventPayload {
@@ -48,4 +36,4 @@ export interface IOrganizationBuildEventPayload {
export interface IOrganizationBuiltEventPayload { export interface IOrganizationBuiltEventPayload {
tenantId: number; tenantId: number;
} }

View File

@@ -1,20 +0,0 @@
export interface StripePaymentLinkCreatedEventPayload {
tenantId: number;
paymentLinkId: string;
saleInvoiceId: number;
stripeIntegrationId: number;
}
export interface StripeCheckoutSessionCompletedEventPayload {
event: any;
}
export interface StripeInvoiceCheckoutSessionPOJO {
sessionId: string;
publishableKey: string;
redirectTo: string;
}
export interface StripeWebhookEventPayload {
event: any;
}

View File

@@ -1,4 +1,4 @@
import { DiscountType, IDynamicListFilter, IItemEntry, IItemEntryDTO } from '@/interfaces'; import { IDynamicListFilter, IItemEntry, IItemEntryDTO } from '@/interfaces';
import { Knex } from 'knex'; import { Knex } from 'knex';
import { AttachmentLinkDTO } from './Attachments'; import { AttachmentLinkDTO } from './Attachments';
@@ -63,11 +63,6 @@ export interface IVendorCreditDTO {
branchId?: number; branchId?: number;
warehouseId?: number; warehouseId?: number;
attachments?: AttachmentLinkDTO[]; attachments?: AttachmentLinkDTO[];
discount?: number;
discountType?: DiscountType;
adjustment?: number;
} }
export interface IVendorCreditCreateDTO extends IVendorCreditDTO {} export interface IVendorCreditCreateDTO extends IVendorCreditDTO {}

View File

@@ -9,9 +9,6 @@ export default class Mail {
subject: string = ''; subject: string = '';
content: string = ''; content: string = '';
to: string | string[]; to: string | string[];
cc: string | string[];
bcc: string | string[];
replyTo: string | string[];
from: string = `${process.env.MAIL_FROM_NAME} ${process.env.MAIL_FROM_ADDRESS}`; from: string = `${process.env.MAIL_FROM_NAME} ${process.env.MAIL_FROM_ADDRESS}`;
data: { [key: string]: string | number }; data: { [key: string]: string | number };
attachments: IMailAttachment[]; attachments: IMailAttachment[];
@@ -23,12 +20,9 @@ export default class Mail {
return { return {
to: this.to, to: this.to,
from: this.from, from: this.from,
cc: this.cc,
bcc: this.bcc,
subject: this.subject, subject: this.subject,
html: this.html, html: this.html,
attachments: this.attachments, attachments: this.attachments,
replyTo: this.replyTo,
}; };
} }
@@ -66,21 +60,6 @@ export default class Mail {
return this; return this;
} }
setCC(cc: string | string[]) {
this.cc = cc;
return this;
}
setBCC(bcc: string | string[]) {
this.bcc = bcc;
return this;
}
setReplyTo(replyTo: string | string[]) {
this.replyTo = replyTo;
return this;
}
/** /**
* Sets from address to the mail. * Sets from address to the mail.
* @param {string} from * @param {string} from
@@ -93,7 +72,7 @@ export default class Mail {
/** /**
* Set attachments to the mail. * Set attachments to the mail.
* @param {IMailAttachment[]} attachments * @param {IMailAttachment[]} attachments
* @returns {Mail} * @returns {Mail}
*/ */
setAttachments(attachments: IMailAttachment[]) { setAttachments(attachments: IMailAttachment[]) {

View File

@@ -117,10 +117,8 @@ import { DisconnectPlaidItemOnAccountDeleted } from '@/services/Banking/BankAcco
import { LoopsEventsSubscriber } from '@/services/Loops/LoopsEventsSubscriber'; import { LoopsEventsSubscriber } from '@/services/Loops/LoopsEventsSubscriber';
import { DeleteUncategorizedTransactionsOnAccountDeleting } from '@/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting'; import { DeleteUncategorizedTransactionsOnAccountDeleting } from '@/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting';
import { SeedInitialDemoAccountDataOnOrgBuild } from '@/services/OneClickDemo/events/SeedInitialDemoAccountData'; import { SeedInitialDemoAccountDataOnOrgBuild } from '@/services/OneClickDemo/events/SeedInitialDemoAccountData';
import { TriggerInvalidateCacheOnSubscriptionChange } from '@/services/Subscription/events/TriggerInvalidateCacheOnSubscriptionChange';
import { EventsTrackerListeners } from '@/services/EventsTracker/events/events'; import { EventsTrackerListeners } from '@/services/EventsTracker/events/events';
import { InvoicePaymentIntegrationSubscriber } from '@/services/Sales/Invoices/subscribers/InvoicePaymentIntegrationSubscriber';
import { StripeWebhooksSubscriber } from '@/services/StripePayment/events/StripeWebhooksSubscriber';
import { SeedStripeAccountsOnOAuthGrantedSubscriber } from '@/services/StripePayment/events/SeedStripeAccounts';
export default () => { export default () => {
return new EventPublisher(); return new EventPublisher();
@@ -254,6 +252,7 @@ export const susbcribers = () => {
// Subscription // Subscription
SubscribeFreeOnSignupCommunity, SubscribeFreeOnSignupCommunity,
SendVerfiyMailOnSignUp, SendVerfiyMailOnSignUp,
TriggerInvalidateCacheOnSubscriptionChange,
// Attachments // Attachments
AttachmentsOnSaleInvoiceCreated, AttachmentsOnSaleInvoiceCreated,
@@ -292,11 +291,6 @@ export const susbcribers = () => {
// Demo Account // Demo Account
SeedInitialDemoAccountDataOnOrgBuild, SeedInitialDemoAccountDataOnOrgBuild,
// Stripe Payment
InvoicePaymentIntegrationSubscriber,
StripeWebhooksSubscriber,
SeedStripeAccountsOnOAuthGrantedSubscriber,
...EventsTrackerListeners ...EventsTrackerListeners
]; ];
}; };

View File

@@ -68,9 +68,6 @@ import { BankRule } from '@/models/BankRule';
import { BankRuleCondition } from '@/models/BankRuleCondition'; import { BankRuleCondition } from '@/models/BankRuleCondition';
import { RecognizedBankTransaction } from '@/models/RecognizedBankTransaction'; import { RecognizedBankTransaction } from '@/models/RecognizedBankTransaction';
import { MatchedBankTransaction } from '@/models/MatchedBankTransaction'; import { MatchedBankTransaction } from '@/models/MatchedBankTransaction';
import { PdfTemplate } from '@/models/PdfTemplate';
import { PaymentIntegration } from '@/models/PaymentIntegration';
import { TransactionPaymentServiceEntry } from '@/models/TransactionPaymentServiceEntry';
export default (knex) => { export default (knex) => {
const models = { const models = {
@@ -142,9 +139,6 @@ export default (knex) => {
BankRuleCondition, BankRuleCondition,
RecognizedBankTransaction, RecognizedBankTransaction,
MatchedBankTransaction, MatchedBankTransaction,
PdfTemplate,
PaymentIntegration,
TransactionPaymentServiceEntry,
}; };
return mapValues(models, (model) => model.bindKnex(knex)); return mapValues(models, (model) => model.bindKnex(knex));
}; };

View File

@@ -1,14 +1,12 @@
import { Model, raw, mixin } from 'objection'; import { Model, raw, mixin } from 'objection';
import { castArray, defaultTo, difference } from 'lodash'; import { castArray, difference } from 'lodash';
import moment from 'moment'; import moment from 'moment';
import * as R from 'ramda';
import TenantModel from 'models/TenantModel'; import TenantModel from 'models/TenantModel';
import BillSettings from './Bill.Settings'; import BillSettings from './Bill.Settings';
import ModelSetting from './ModelSetting'; import ModelSetting from './ModelSetting';
import CustomViewBaseModel from './CustomViewBaseModel'; import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from '@/services/Purchases/Bills/constants'; import { DEFAULT_VIEWS } from '@/services/Purchases/Bills/constants';
import ModelSearchable from './ModelSearchable'; import ModelSearchable from './ModelSearchable';
import { DiscountType } from '@/interfaces';
export default class Bill extends mixin(TenantModel, [ export default class Bill extends mixin(TenantModel, [
ModelSetting, ModelSetting,
@@ -23,11 +21,6 @@ export default class Bill extends mixin(TenantModel, [
public taxAmountWithheld: number; public taxAmountWithheld: number;
public exchangeRate: number; public exchangeRate: number;
public discount: number;
public discountType: DiscountType;
public adjustment: number;
/** /**
* Timestamps columns. * Timestamps columns.
*/ */
@@ -54,13 +47,6 @@ export default class Bill extends mixin(TenantModel, [
'localAllocatedCostAmount', 'localAllocatedCostAmount',
'billableAmount', 'billableAmount',
'amountLocal', 'amountLocal',
'discountAmount',
'discountAmountLocal',
'discountPercentage',
'adjustmentLocal',
'subtotal', 'subtotal',
'subtotalLocal', 'subtotalLocal',
'subtotalExludingTax', 'subtotalExludingTax',
@@ -112,53 +98,14 @@ export default class Bill extends mixin(TenantModel, [
return this.taxAmountWithheld * this.exchangeRate; return this.taxAmountWithheld * this.exchangeRate;
} }
/**
* Discount amount.
* @returns {number}
*/
get discountAmount() {
return this.discountType === DiscountType.Amount
? this.discount
: this.subtotal * (this.discount / 100);
}
/**
* Discount amount in local currency.
* @returns {number | null}
*/
get discountAmountLocal() {
return this.discountAmount ? this.discountAmount * this.exchangeRate : null;
}
/**
/**
* Discount percentage.
* @returns {number | null}
*/
get discountPercentage(): number | null {
return this.discountType === DiscountType.Percentage ? this.discount : null;
}
/**
* Adjustment amount in local currency.
* @returns {number | null}
*/
get adjustmentLocal() {
return this.adjustment ? this.adjustment * this.exchangeRate : null;
}
/** /**
* Invoice total. (Tax included) * Invoice total. (Tax included)
* @returns {number} * @returns {number}
*/ */
get total() { get total() {
const adjustmentAmount = defaultTo(this.adjustment, 0); return this.isInclusiveTax
? this.subtotal
return R.compose( : this.subtotal + this.taxAmountWithheld;
R.add(adjustmentAmount),
R.subtract(R.__, this.discountAmount),
R.when(R.always(this.isInclusiveTax), R.add(this.taxAmountWithheld))
)(this.subtotal);
} }
/** /**

View File

@@ -5,20 +5,12 @@ import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from '@/services/CreditNotes/constants'; import { DEFAULT_VIEWS } from '@/services/CreditNotes/constants';
import ModelSearchable from './ModelSearchable'; import ModelSearchable from './ModelSearchable';
import CreditNoteMeta from './CreditNote.Meta'; import CreditNoteMeta from './CreditNote.Meta';
import { DiscountType } from '@/interfaces';
export default class CreditNote extends mixin(TenantModel, [ export default class CreditNote extends mixin(TenantModel, [
ModelSetting, ModelSetting,
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable, ModelSearchable,
]) { ]) {
public amount: number;
public exchangeRate: number;
public openedAt: Date;
public discount: number;
public discountType: DiscountType;
public adjustment: number;
/** /**
* Table name * Table name
*/ */
@@ -43,21 +35,8 @@ export default class CreditNote extends mixin(TenantModel, [
'isPublished', 'isPublished',
'isOpen', 'isOpen',
'isClosed', 'isClosed',
'creditsRemaining', 'creditsRemaining',
'creditsUsed', 'creditsUsed',
'subtotal',
'subtotalLocal',
'discountAmount',
'discountAmountLocal',
'discountPercentage',
'total',
'totalLocal',
'adjustmentLocal',
]; ];
} }
@@ -69,72 +48,6 @@ export default class CreditNote extends mixin(TenantModel, [
return this.amount * this.exchangeRate; return this.amount * this.exchangeRate;
} }
/**
* Credit note subtotal.
* @returns {number}
*/
get subtotal() {
return this.amount;
}
/**
* Credit note subtotal in local currency.
* @returns {number}
*/
get subtotalLocal() {
return this.subtotal * this.exchangeRate;
}
/**
* Discount amount.
* @returns {number}
*/
get discountAmount() {
return this.discountType === DiscountType.Amount
? this.discount
: this.subtotal * (this.discount / 100);
}
/**
* Discount amount in local currency.
* @returns {number}
*/
get discountAmountLocal() {
return this.discountAmount ? this.discountAmount * this.exchangeRate : null;
}
/**
* Discount percentage.
* @returns {number | null}
*/
get discountPercentage(): number | null {
return this.discountType === DiscountType.Percentage ? this.discount : null;
}
/**
* Adjustment amount in local currency.
* @returns {number}
*/
get adjustmentLocal() {
return this.adjustment ? this.adjustment * this.exchangeRate : null;
}
/**
* Credit note total.
* @returns {number}
*/
get total() {
return this.subtotal - this.discountAmount + this.adjustment;
}
/**
* Credit note total in local currency.
* @returns {number}
*/
get totalLocal() {
return this.total * this.exchangeRate;
}
/** /**
* Detarmines whether the credit note is draft. * Detarmines whether the credit note is draft.
* @returns {boolean} * @returns {boolean}
@@ -263,7 +176,6 @@ export default class CreditNote extends mixin(TenantModel, [
const Branch = require('models/Branch'); const Branch = require('models/Branch');
const Document = require('models/Document'); const Document = require('models/Document');
const Warehouse = require('models/Warehouse'); const Warehouse = require('models/Warehouse');
const { PdfTemplate } = require('models/PdfTemplate');
return { return {
/** /**
@@ -354,18 +266,6 @@ export default class CreditNote extends mixin(TenantModel, [
query.where('model_ref', 'CreditNote'); query.where('model_ref', 'CreditNote');
}, },
}, },
/**
* Credit note may belongs to pdf branding template.
*/
pdfTemplate: {
relation: Model.BelongsToOneRelation,
modelClass: PdfTemplate,
join: {
from: 'credit_notes.pdfTemplateId',
to: 'pdf_templates.id',
},
},
}; };
} }

View File

@@ -294,7 +294,7 @@ export default {
name: 'item.field.note', name: 'item.field.note',
fieldType: 'text', fieldType: 'text',
}, },
categoryId: { category: {
name: 'item.field.category', name: 'item.field.category',
fieldType: 'relation', fieldType: 'relation',
relationModel: 'ItemCategory', relationModel: 'ItemCategory',

View File

@@ -1,12 +1,6 @@
import { Model } from 'objection'; import { Model } from 'objection';
import TenantModel from 'models/TenantModel'; import TenantModel from 'models/TenantModel';
import { getExlusiveTaxAmount, getInclusiveTaxAmount } from '@/utils/taxRate'; import { getExlusiveTaxAmount, getInclusiveTaxAmount } from '@/utils/taxRate';
import { DiscountType } from '@/interfaces';
// Subtotal (qty * rate) (tax inclusive)
// Subtotal Tax Exclusive (Subtotal - Tax Amount)
// Discount (Is percentage ? amount * discount : discount)
// Total (Subtotal - Discount)
export default class ItemEntry extends TenantModel { export default class ItemEntry extends TenantModel {
public taxRate: number; public taxRate: number;
@@ -14,7 +8,7 @@ export default class ItemEntry extends TenantModel {
public quantity: number; public quantity: number;
public rate: number; public rate: number;
public isInclusiveTax: number; public isInclusiveTax: number;
public discountType: DiscountType;
/** /**
* Table name. * Table name.
* @returns {string} * @returns {string}
@@ -37,24 +31,10 @@ export default class ItemEntry extends TenantModel {
*/ */
static get virtualAttributes() { static get virtualAttributes() {
return [ return [
// Amount (qty * rate)
'amount', 'amount',
'taxAmount', 'taxAmount',
'amountExludingTax',
// Subtotal (qty * rate) + (tax inclusive) 'amountInclusingTax',
'subtotalInclusingTax',
// Subtotal Tax Exclusive (Subtotal - Tax Amount)
'subtotalExcludingTax',
// Subtotal (qty * rate) + (tax inclusive)
'subtotal',
// Discount (Is percentage ? amount * discount : discount)
'discountAmount',
// Total (Subtotal - Discount)
'total', 'total',
]; ];
} }
@@ -65,15 +45,7 @@ export default class ItemEntry extends TenantModel {
* @returns {number} * @returns {number}
*/ */
get total() { get total() {
return this.subtotal - this.discountAmount; return this.amountInclusingTax;
}
/**
* Total (excluding tax).
* @returns {number}
*/
get totalExcludingTax() {
return this.subtotalExcludingTax - this.discountAmount;
} }
/** /**
@@ -85,27 +57,19 @@ export default class ItemEntry extends TenantModel {
return this.quantity * this.rate; return this.quantity * this.rate;
} }
/**
* Subtotal amount (tax inclusive).
* @returns {number}
*/
get subtotal() {
return this.subtotalInclusingTax;
}
/** /**
* Item entry amount including tax. * Item entry amount including tax.
* @returns {number} * @returns {number}
*/ */
get subtotalInclusingTax() { get amountInclusingTax() {
return this.isInclusiveTax ? this.amount : this.amount + this.taxAmount; return this.isInclusiveTax ? this.amount : this.amount + this.taxAmount;
} }
/** /**
* Subtotal amount (tax exclusive). * Item entry amount excluding tax.
* @returns {number} * @returns {number}
*/ */
get subtotalExcludingTax() { get amountExludingTax() {
return this.isInclusiveTax ? this.amount - this.taxAmount : this.amount; return this.isInclusiveTax ? this.amount - this.taxAmount : this.amount;
} }
@@ -114,9 +78,7 @@ export default class ItemEntry extends TenantModel {
* @returns {number} * @returns {number}
*/ */
get discountAmount() { get discountAmount() {
return this.discountType === DiscountType.Percentage return this.amount * (this.discount / 100);
? this.amount * (this.discount / 100)
: this.discount;
} }
/** /**

View File

@@ -1,61 +0,0 @@
import { Model } from 'objection';
import TenantModel from 'models/TenantModel';
export class PaymentIntegration extends Model {
paymentEnabled!: boolean;
payoutEnabled!: boolean;
static get tableName() {
return 'payment_integrations';
}
static get idColumn() {
return 'id';
}
static get virtualAttributes() {
return ['fullEnabled'];
}
static get jsonAttributes() {
return ['options'];
}
get fullEnabled() {
return this.paymentEnabled && this.payoutEnabled;
}
static get modifiers() {
return {
/**
* Query to filter enabled payment and payout.
*/
fullEnabled(query) {
query.where('paymentEnabled', true).andWhere('payoutEnabled', true);
},
};
}
static get jsonSchema() {
return {
type: 'object',
required: ['name', 'service'],
properties: {
id: { type: 'integer' },
service: { type: 'string' },
paymentEnabled: { type: 'boolean' },
payoutEnabled: { type: 'boolean' },
accountId: { type: 'string' },
options: {
type: 'object',
properties: {
bankAccountId: { type: 'number' },
clearingAccountId: { type: 'number' },
},
},
createdAt: { type: 'string', format: 'date-time' },
updatedAt: { type: 'string', format: 'date-time' },
},
};
}
}

View File

@@ -11,10 +11,6 @@ export default class PaymentReceive extends mixin(TenantModel, [
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable, ModelSearchable,
]) { ]) {
amount!: number;
paymentAmount!: number;
exchangeRate!: number;
/** /**
* Table name. * Table name.
*/ */
@@ -33,7 +29,7 @@ export default class PaymentReceive extends mixin(TenantModel, [
* Virtual attributes. * Virtual attributes.
*/ */
static get virtualAttributes() { static get virtualAttributes() {
return ['localAmount', 'total']; return ['localAmount'];
} }
/** /**
@@ -44,14 +40,6 @@ export default class PaymentReceive extends mixin(TenantModel, [
return this.amount * this.exchangeRate; return this.amount * this.exchangeRate;
} }
/**
* Payment receive total.
* @returns {number}
*/
get total() {
return this.paymentAmount;
}
/** /**
* Resourcable model. * Resourcable model.
*/ */
@@ -69,7 +57,6 @@ export default class PaymentReceive extends mixin(TenantModel, [
const Account = require('models/Account'); const Account = require('models/Account');
const Branch = require('models/Branch'); const Branch = require('models/Branch');
const Document = require('models/Document'); const Document = require('models/Document');
const { PdfTemplate } = require('models/PdfTemplate');
return { return {
customer: { customer: {
@@ -144,18 +131,6 @@ export default class PaymentReceive extends mixin(TenantModel, [
query.where('model_ref', 'PaymentReceive'); query.where('model_ref', 'PaymentReceive');
}, },
}, },
/**
* Payment received may belongs to pdf branding template.
*/
pdfTemplate: {
relation: Model.BelongsToOneRelation,
modelClass: PdfTemplate,
join: {
from: 'payment_receives.pdfTemplateId',
to: 'pdf_templates.id',
},
},
}; };
} }

View File

@@ -1,72 +0,0 @@
import { getUploadedObjectUri } from '@/services/Attachments/utils';
import TenantModel from 'models/TenantModel';
export class PdfTemplate extends TenantModel {
public readonly attributes: Record<string, any>;
/**
* Table name.
*/
static get tableName() {
return 'pdf_templates';
}
/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt', 'updatedAt'];
}
/**
* Json schema.
*/
static get jsonSchema() {
return {
type: 'object',
properties: {
id: { type: 'integer' },
templateName: { type: 'string' },
attributes: { type: 'object' }, // JSON field definition
},
};
}
/**
* Model modifiers.
*/
static get modifiers() {
return {
/**
* Filters the due invoices.
*/
default(query) {
query.where('default', true);
},
};
}
/**
* Virtual attributes.
*/
static get virtualAttributes() {
return ['companyLogoUri'];
}
/**
* Retrieves the company logo uri.
* @returns {string}
*/
get companyLogoUri() {
return this.attributes?.companyLogoKey
? getUploadedObjectUri(this.attributes.companyLogoKey)
: '';
}
/**
* Relationship mapping.
*/
static get relationMappings() {
return {};
}
}

View File

@@ -7,30 +7,12 @@ import ModelSetting from './ModelSetting';
import CustomViewBaseModel from './CustomViewBaseModel'; import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from '@/services/Sales/Estimates/constants'; import { DEFAULT_VIEWS } from '@/services/Sales/Estimates/constants';
import ModelSearchable from './ModelSearchable'; import ModelSearchable from './ModelSearchable';
import { DiscountType } from '@/interfaces';
import { defaultTo } from 'lodash';
export default class SaleEstimate extends mixin(TenantModel, [ export default class SaleEstimate extends mixin(TenantModel, [
ModelSetting, ModelSetting,
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable, ModelSearchable,
]) { ]) {
public amount: number;
public exchangeRate: number;
public discount: number;
public discountType: DiscountType;
public adjustment: number;
public expirationDate!: string;
public deliveredAt!: string | null;
public approvedAt!: string | null;
public rejectedAt!: string | null;
public convertedToInvoiceId!: number | null;
public convertedToInvoiceAt!: string | null;
/** /**
* Table name * Table name
*/ */
@@ -51,12 +33,6 @@ export default class SaleEstimate extends mixin(TenantModel, [
static get virtualAttributes() { static get virtualAttributes() {
return [ return [
'localAmount', 'localAmount',
'discountAmount',
'discountPercentage',
'total',
'totalLocal',
'subtotal',
'subtotalLocal',
'isDelivered', 'isDelivered',
'isExpired', 'isExpired',
'isConvertedToInvoice', 'isConvertedToInvoice',
@@ -73,60 +49,6 @@ export default class SaleEstimate extends mixin(TenantModel, [
return this.amount * this.exchangeRate; return this.amount * this.exchangeRate;
} }
/**
* Estimate subtotal.
* @returns {number}
*/
get subtotal() {
return this.amount;;
}
/**
* Estimate subtotal in local currency.
* @returns {number}
*/
get subtotalLocal() {
return this.localAmount;
}
/**
* Discount amount.
* @returns {number}
*/
get discountAmount() {
return this.discountType === DiscountType.Amount
? this.discount
: this.subtotal * (this.discount / 100);
}
/**
* Discount percentage.
* @returns {number | null}
*/
get discountPercentage(): number | null {
return this.discountType === DiscountType.Percentage
? this.discount
: null;
}
/**
* Estimate total.
* @returns {number}
*/
get total() {
const adjustmentAmount = defaultTo(this.adjustment, 0);
return this.subtotal - this.discountAmount + adjustmentAmount;
}
/**
* Estimate total in local currency.
* @returns {number}
*/
get totalLocal() {
return this.total * this.exchangeRate;
}
/** /**
* Detarmines whether the sale estimate converted to sale invoice. * Detarmines whether the sale estimate converted to sale invoice.
* @return {boolean} * @return {boolean}
@@ -262,7 +184,6 @@ export default class SaleEstimate extends mixin(TenantModel, [
const Branch = require('models/Branch'); const Branch = require('models/Branch');
const Warehouse = require('models/Warehouse'); const Warehouse = require('models/Warehouse');
const Document = require('models/Document'); const Document = require('models/Document');
const { PdfTemplate } = require('models/PdfTemplate');
return { return {
customer: { customer: {
@@ -331,18 +252,6 @@ export default class SaleEstimate extends mixin(TenantModel, [
query.where('model_ref', 'SaleEstimate'); query.where('model_ref', 'SaleEstimate');
}, },
}, },
/**
* Sale estimate may belongs to pdf branding template.
*/
pdfTemplate: {
relation: Model.BelongsToOneRelation,
modelClass: PdfTemplate,
join: {
from: 'sales_estimates.pdfTemplateId',
to: 'pdf_templates.id',
},
},
}; };
} }

View File

@@ -1,6 +1,5 @@
import { mixin, Model, raw } from 'objection'; import { mixin, Model, raw } from 'objection';
import * as R from 'ramda'; import { castArray, takeWhile } from 'lodash';
import { castArray, defaultTo, takeWhile } from 'lodash';
import moment from 'moment'; import moment from 'moment';
import TenantModel from 'models/TenantModel'; import TenantModel from 'models/TenantModel';
import ModelSetting from './ModelSetting'; import ModelSetting from './ModelSetting';
@@ -8,7 +7,6 @@ import SaleInvoiceMeta from './SaleInvoice.Settings';
import CustomViewBaseModel from './CustomViewBaseModel'; import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from '@/services/Sales/Invoices/constants'; import { DEFAULT_VIEWS } from '@/services/Sales/Invoices/constants';
import ModelSearchable from './ModelSearchable'; import ModelSearchable from './ModelSearchable';
import { DiscountType } from '@/interfaces';
export default class SaleInvoice extends mixin(TenantModel, [ export default class SaleInvoice extends mixin(TenantModel, [
ModelSetting, ModelSetting,
@@ -25,9 +23,6 @@ export default class SaleInvoice extends mixin(TenantModel, [
public writtenoffAt: Date; public writtenoffAt: Date;
public dueDate: Date; public dueDate: Date;
public deliveredAt: Date; public deliveredAt: Date;
public discount: number;
public discountType: DiscountType;
public adjustment: number | null;
/** /**
* Table name * Table name
@@ -72,15 +67,10 @@ export default class SaleInvoice extends mixin(TenantModel, [
'subtotalExludingTax', 'subtotalExludingTax',
'taxAmountWithheldLocal', 'taxAmountWithheldLocal',
'discountAmount',
'discountAmountLocal',
'discountPercentage',
'total', 'total',
'totalLocal', 'totalLocal',
'writtenoffAmountLocal', 'writtenoffAmountLocal',
'adjustmentLocal',
]; ];
} }
@@ -135,52 +125,14 @@ export default class SaleInvoice extends mixin(TenantModel, [
return this.taxAmountWithheld * this.exchangeRate; return this.taxAmountWithheld * this.exchangeRate;
} }
/**
* Discount amount.
* @returns {number}
*/
get discountAmount() {
return this.discountType === DiscountType.Amount
? this.discount
: this.subtotal * (this.discount / 100);
}
/**
* Local discount amount.
* @returns {number | null}
*/
get discountAmountLocal() {
return this.discountAmount ? this.discountAmount * this.exchangeRate : null;
}
/**
* Discount percentage.
* @returns {number | null}
*/
get discountPercentage(): number | null {
return this.discountType === DiscountType.Percentage ? this.discount : null;
}
/**
* Adjustment amount in local currency.
* @returns {number | null}
*/
get adjustmentLocal(): number | null {
return this.adjustment ? this.adjustment * this.exchangeRate : null;
}
/** /**
* Invoice total. (Tax included) * Invoice total. (Tax included)
* @returns {number} * @returns {number}
*/ */
get total() { get total() {
const adjustmentAmount = defaultTo(this.adjustment, 0); return this.isInclusiveTax
? this.subtotal
return R.compose( : this.subtotal + this.taxAmountWithheld;
R.add(adjustmentAmount),
R.subtract(R.__, this.discountAmount),
R.when(R.always(this.isInclusiveTax), R.add(this.taxAmountWithheld))
)(this.subtotal);
} }
/** /**
@@ -461,10 +413,6 @@ export default class SaleInvoice extends mixin(TenantModel, [
const TaxRateTransaction = require('models/TaxRateTransaction'); const TaxRateTransaction = require('models/TaxRateTransaction');
const Document = require('models/Document'); const Document = require('models/Document');
const { MatchedBankTransaction } = require('models/MatchedBankTransaction'); const { MatchedBankTransaction } = require('models/MatchedBankTransaction');
const {
TransactionPaymentServiceEntry,
} = require('models/TransactionPaymentServiceEntry');
const { PdfTemplate } = require('models/PdfTemplate');
return { return {
/** /**
@@ -561,7 +509,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
join: { join: {
from: 'sales_invoices.warehouseId', from: 'sales_invoices.warehouseId',
to: 'warehouses.id', to: 'warehouses.id',
}, }
}, },
/** /**
@@ -618,42 +566,12 @@ export default class SaleInvoice extends mixin(TenantModel, [
modelClass: MatchedBankTransaction, modelClass: MatchedBankTransaction,
join: { join: {
from: 'sales_invoices.id', from: 'sales_invoices.id',
to: 'matched_bank_transactions.referenceId', to: "matched_bank_transactions.referenceId",
}, },
filter(query) { filter(query) {
query.where('reference_type', 'SaleInvoice'); query.where('reference_type', 'SaleInvoice');
}, },
}, },
/**
* Sale invoice may belongs to payment methods entries.
*/
paymentMethods: {
relation: Model.HasManyRelation,
modelClass: TransactionPaymentServiceEntry,
join: {
from: 'sales_invoices.id',
to: 'transactions_payment_methods.referenceId',
},
beforeInsert: (model) => {
model.referenceType = 'SaleInvoice';
},
filter: (query) => {
query.where('reference_type', 'SaleInvoice');
},
},
/**
* Sale invoice may belongs to pdf branding template.
*/
pdfTemplate: {
relation: Model.BelongsToOneRelation,
modelClass: PdfTemplate,
join: {
from: 'sales_invoices.pdfTemplateId',
to: 'pdf_templates.id',
},
},
}; };
} }

View File

@@ -5,23 +5,12 @@ import SaleReceiptSettings from './SaleReceipt.Settings';
import CustomViewBaseModel from './CustomViewBaseModel'; import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from '@/services/Sales/Receipts/constants'; import { DEFAULT_VIEWS } from '@/services/Sales/Receipts/constants';
import ModelSearchable from './ModelSearchable'; import ModelSearchable from './ModelSearchable';
import { DiscountType } from '@/interfaces';
import { defaultTo } from 'lodash';
export default class SaleReceipt extends mixin(TenantModel, [ export default class SaleReceipt extends mixin(TenantModel, [
ModelSetting, ModelSetting,
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable, ModelSearchable,
]) { ]) {
public amount: number;
public exchangeRate: number;
public closedAt: Date;
public discount: number;
public discountType: DiscountType;
public adjustment: number;
/** /**
* Table name * Table name
*/ */
@@ -40,28 +29,7 @@ export default class SaleReceipt extends mixin(TenantModel, [
* Virtual attributes. * Virtual attributes.
*/ */
static get virtualAttributes() { static get virtualAttributes() {
return [ return ['localAmount', 'isClosed', 'isDraft'];
'localAmount',
'subtotal',
'subtotalLocal',
'total',
'totalLocal',
'adjustment',
'adjustmentLocal',
'discountAmount',
'discountAmountLocal',
'discountPercentage',
'paid',
'paidLocal',
'isClosed',
'isDraft',
];
} }
/** /**
@@ -72,90 +40,6 @@ export default class SaleReceipt extends mixin(TenantModel, [
return this.amount * this.exchangeRate; return this.amount * this.exchangeRate;
} }
/**
* Receipt subtotal.
* @returns {number}
*/
get subtotal() {
return this.amount;
}
/**
* Receipt subtotal in local currency.
* @returns {number}
*/
get subtotalLocal() {
return this.localAmount;
}
/**
* Discount amount.
* @returns {number}
*/
get discountAmount() {
return this.discountType === DiscountType.Amount
? this.discount
: this.subtotal * (this.discount / 100);
}
/**
* Discount amount in local currency.
* @returns {number | null}
*/
get discountAmountLocal() {
return this.discountAmount ? this.discountAmount * this.exchangeRate : null;
}
/**
* Discount percentage.
* @returns {number | null}
*/
get discountPercentage(): number | null {
return this.discountType === DiscountType.Percentage ? this.discount : null;
}
/**
* Receipt total.
* @returns {number}
*/
get total() {
const adjustmentAmount = defaultTo(this.adjustment, 0);
return this.subtotal - this.discountAmount + adjustmentAmount;
}
/**
* Receipt total in local currency.
* @returns {number}
*/
get totalLocal() {
return this.total * this.exchangeRate;
}
/**
* Adjustment amount in local currency.
* @returns {number}
*/
get adjustmentLocal() {
return this.adjustment * this.exchangeRate;
}
/**
* Receipt paid amount.
* @returns {number}
*/
get paid() {
return this.total;
}
/**
* Receipt paid amount in local currency.
* @returns {number}
*/
get paidLocal() {
return this.paid * this.exchangeRate;
}
/** /**
* Detarmine whether the sale receipt closed. * Detarmine whether the sale receipt closed.
* @return {boolean} * @return {boolean}

View File

@@ -1,46 +0,0 @@
import TenantModel from 'models/TenantModel';
export class TransactionPaymentServiceEntry extends TenantModel {
/**
* Table name
*/
static get tableName() {
return 'transactions_payment_methods';
}
/**
* Json schema of the model.
*/
static get jsonSchema() {
return {
type: 'object',
required: ['paymentIntegrationId'],
properties: {
id: { type: 'integer' },
referenceId: { type: 'integer' },
referenceType: { type: 'string' },
paymentIntegrationId: { type: 'integer' },
enable: { type: 'boolean' },
options: { type: 'object' },
},
};
}
/**
* Relationship mapping.
*/
static get relationMappings() {
const { PaymentIntegration } = require('./PaymentIntegration');
return {
paymentIntegration: {
relation: TenantModel.BelongsToOneRelation,
modelClass: PaymentIntegration,
join: {
from: 'transactions_payment_methods.paymentIntegrationId',
to: 'payment_integrations.id',
},
},
};
}
}

View File

@@ -11,12 +11,6 @@ export default class UncategorizedCashflowTransaction extends mixin(
) { ) {
id!: number; id!: number;
date!: Date | string; date!: Date | string;
/**
* Transaction amount.
* Negative represents to spending and positive to deposit/card charge.
* @param {number}
*/
amount!: number; amount!: number;
categorized!: boolean; categorized!: boolean;
accountId!: number; accountId!: number;

View File

@@ -6,26 +6,26 @@ import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from '@/services/Purchases/VendorCredits/constants'; import { DEFAULT_VIEWS } from '@/services/Purchases/VendorCredits/constants';
import ModelSearchable from './ModelSearchable'; import ModelSearchable from './ModelSearchable';
import VendorCreditMeta from './VendorCredit.Meta'; import VendorCreditMeta from './VendorCredit.Meta';
import { DiscountType } from '@/interfaces';
export default class VendorCredit extends mixin(TenantModel, [ export default class VendorCredit extends mixin(TenantModel, [
ModelSetting, ModelSetting,
CustomViewBaseModel, CustomViewBaseModel,
ModelSearchable, ModelSearchable,
]) { ]) {
public amount: number;
public exchangeRate: number;
public openedAt: Date;
public discount: number;
public discountType: DiscountType;
public adjustment: number;
/** /**
* Table name * Table name
*/ */
static get tableName() { static get tableName() {
return 'vendor_credits'; return 'vendor_credits';
} }
/**
* Virtual attributes.
*/
static get virtualAttributes() {
return ['localAmount'];
}
/** /**
* Vendor credit amount in local currency. * Vendor credit amount in local currency.
* @returns {number} * @returns {number}
@@ -34,72 +34,6 @@ export default class VendorCredit extends mixin(TenantModel, [
return this.amount * this.exchangeRate; return this.amount * this.exchangeRate;
} }
/**
* Vendor credit subtotal.
* @returns {number}
*/
get subtotal() {
return this.amount;
}
/**
* Vendor credit subtotal in local currency.
* @returns {number}
*/
get subtotalLocal() {
return this.subtotal * this.exchangeRate;
}
/**
* Discount amount.
* @returns {number}
*/
get discountAmount() {
return this.discountType === DiscountType.Amount
? this.discount
: this.subtotal * (this.discount / 100);
}
/**
* Discount amount in local currency.
* @returns {number | null}
*/
get discountAmountLocal() {
return this.discountAmount ? this.discountAmount * this.exchangeRate : null;
}
/**
* Discount percentage.
* @returns {number | null}
*/
get discountPercentage(): number | null {
return this.discountType === DiscountType.Percentage ? this.discount : null;
}
/**
* Adjustment amount in local currency.
* @returns {number | null}
*/
get adjustmentLocal() {
return this.adjustment ? this.adjustment * this.exchangeRate : null;
}
/**
* Vendor credit total.
* @returns {number}
*/
get total() {
return this.subtotal - this.discountAmount + this.adjustment;
}
/**
* Vendor credit total in local currency.
* @returns {number}
*/
get totalLocal() {
return this.total * this.exchangeRate;
}
/** /**
* Model modifiers. * Model modifiers.
*/ */
@@ -186,24 +120,7 @@ export default class VendorCredit extends mixin(TenantModel, [
* Virtual attributes. * Virtual attributes.
*/ */
static get virtualAttributes() { static get virtualAttributes() {
return [ return ['isDraft', 'isPublished', 'isOpen', 'isClosed', 'creditsRemaining'];
'isDraft',
'isPublished',
'isOpen',
'isClosed',
'creditsRemaining',
'localAmount',
'discountAmount',
'discountAmountLocal',
'discountPercentage',
'adjustmentLocal',
'total',
'totalLocal',
];
} }
/** /**

View File

@@ -3,12 +3,7 @@ import TenantRepository from '@/repositories/TenantRepository';
import { IAccount } from '@/interfaces'; import { IAccount } from '@/interfaces';
import { Knex } from 'knex'; import { Knex } from 'knex';
import { import {
DiscountExpenseAccount,
OtherChargesAccount,
OtherExpensesAccount,
PrepardExpenses, PrepardExpenses,
PurchaseDiscountAccount,
StripeClearingAccount,
TaxPayableAccount, TaxPayableAccount,
UnearnedRevenueAccount, UnearnedRevenueAccount,
} from '@/database/seeds/data/accounts'; } from '@/database/seeds/data/accounts';
@@ -192,9 +187,9 @@ export default class AccountRepository extends TenantRepository {
/** /**
* Finds or creates the unearned revenue. * Finds or creates the unearned revenue.
* @param {Record<string, string>} extraAttrs * @param {Record<string, string>} extraAttrs
* @param {Knex.Transaction} trx * @param {Knex.Transaction} trx
* @returns * @returns
*/ */
public async findOrCreateUnearnedRevenue( public async findOrCreateUnearnedRevenue(
extraAttrs: Record<string, string> = {}, extraAttrs: Record<string, string> = {},
@@ -223,9 +218,9 @@ export default class AccountRepository extends TenantRepository {
/** /**
* Finds or creates the prepard expenses account. * Finds or creates the prepard expenses account.
* @param {Record<string, string>} extraAttrs * @param {Record<string, string>} extraAttrs
* @param {Knex.Transaction} trx * @param {Knex.Transaction} trx
* @returns * @returns
*/ */
public async findOrCreatePrepardExpenses( public async findOrCreatePrepardExpenses(
extraAttrs: Record<string, string> = {}, extraAttrs: Record<string, string> = {},
@@ -252,146 +247,4 @@ export default class AccountRepository extends TenantRepository {
} }
return result; return result;
} }
/**
* Finds or creates the stripe clearing account.
* @param {Record<string, string>} extraAttrs
* @param {Knex.Transaction} trx
* @returns
*/
public async findOrCreateStripeClearing(
extraAttrs: Record<string, string> = {},
trx?: Knex.Transaction
) {
// Retrieves the given tenant metadata.
const tenantMeta = await TenantMetadata.query().findOne({
tenantId: this.tenantId,
});
const _extraAttrs = {
currencyCode: tenantMeta.baseCurrency,
...extraAttrs,
};
let result = await this.model
.query(trx)
.findOne({ slug: StripeClearingAccount.slug, ..._extraAttrs });
if (!result) {
result = await this.model.query(trx).insertAndFetch({
...StripeClearingAccount,
..._extraAttrs,
});
}
return result;
}
/**
* Finds or creates the discount expense account.
* @param {Record<string, string>} extraAttrs
* @param {Knex.Transaction} trx
* @returns
*/
public async findOrCreateDiscountAccount(
extraAttrs: Record<string, string> = {},
trx?: Knex.Transaction
) {
// Retrieves the given tenant metadata.
const tenantMeta = await TenantMetadata.query().findOne({
tenantId: this.tenantId,
});
const _extraAttrs = {
currencyCode: tenantMeta.baseCurrency,
...extraAttrs,
};
let result = await this.model
.query(trx)
.findOne({ slug: DiscountExpenseAccount.slug, ..._extraAttrs });
if (!result) {
result = await this.model.query(trx).insertAndFetch({
...DiscountExpenseAccount,
..._extraAttrs,
});
}
return result;
}
public async findOrCreatePurchaseDiscountAccount(
extraAttrs: Record<string, string> = {},
trx?: Knex.Transaction
) {
// Retrieves the given tenant metadata.
const tenantMeta = await TenantMetadata.query().findOne({
tenantId: this.tenantId,
});
const _extraAttrs = {
currencyCode: tenantMeta.baseCurrency,
...extraAttrs,
};
let result = await this.model
.query(trx)
.findOne({ slug: PurchaseDiscountAccount.slug, ..._extraAttrs });
if (!result) {
result = await this.model.query(trx).insertAndFetch({
...PurchaseDiscountAccount,
..._extraAttrs,
});
}
return result;
}
public async findOrCreateOtherChargesAccount(
extraAttrs: Record<string, string> = {},
trx?: Knex.Transaction
) {
// Retrieves the given tenant metadata.
const tenantMeta = await TenantMetadata.query().findOne({
tenantId: this.tenantId,
});
const _extraAttrs = {
currencyCode: tenantMeta.baseCurrency,
...extraAttrs,
};
let result = await this.model
.query(trx)
.findOne({ slug: OtherChargesAccount.slug, ..._extraAttrs });
if (!result) {
result = await this.model.query(trx).insertAndFetch({
...OtherChargesAccount,
..._extraAttrs,
});
}
return result;
}
public async findOrCreateOtherExpensesAccount(
extraAttrs: Record<string, string> = {},
trx?: Knex.Transaction
) {
// Retrieves the given tenant metadata.
const tenantMeta = await TenantMetadata.query().findOne({
tenantId: this.tenantId,
});
const _extraAttrs = {
currencyCode: tenantMeta.baseCurrency,
...extraAttrs,
};
let result = await this.model
.query(trx)
.findOne({ slug: OtherExpensesAccount.slug, ..._extraAttrs });
if (!result) {
result = await this.model.query(trx).insertAndFetch({
...OtherExpensesAccount,
..._extraAttrs,
});
}
return result;
}
} }

View File

@@ -238,7 +238,6 @@ export default class Ledger implements ILedger {
return { return {
credit: defaultTo(entry.credit, 0), credit: defaultTo(entry.credit, 0),
debit: defaultTo(entry.debit, 0), debit: defaultTo(entry.debit, 0),
exchangeRate: entry.exchangeRate, exchangeRate: entry.exchangeRate,
currencyCode: entry.currencyCode, currencyCode: entry.currencyCode,

View File

@@ -9,18 +9,15 @@ import {
import HasTenancyService from '@/services/Tenancy/TenancyService'; import HasTenancyService from '@/services/Tenancy/TenancyService';
import { transformLedgerEntryToTransaction } from './utils'; import { transformLedgerEntryToTransaction } from './utils';
// Filter the blank entries.
const filterBlankEntry = (entry: ILedgerEntry) => Boolean(entry.credit || entry.debit);
@Service() @Service()
export class LedgerEntriesStorage { export class LedgerEntriesStorage {
@Inject() @Inject()
private tenancy: HasTenancyService; tenancy: HasTenancyService;
/** /**
* Saves entries of the given ledger. * Saves entries of the given ledger.
* @param {number} tenantId * @param {number} tenantId
* @param {ILedger} ledger * @param {ILedger} ledger
* @param {Knex.Transaction} knex * @param {Knex.Transaction} knex
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public saveEntries = async ( public saveEntries = async (
@@ -29,7 +26,7 @@ export class LedgerEntriesStorage {
trx?: Knex.Transaction trx?: Knex.Transaction
) => { ) => {
const saveEntryQueue = async.queue(this.saveEntryTask, 10); const saveEntryQueue = async.queue(this.saveEntryTask, 10);
const entries = ledger.filter(filterBlankEntry).getEntries(); const entries = ledger.getEntries();
entries.forEach((entry) => { entries.forEach((entry) => {
saveEntryQueue.push({ tenantId, entry, trx }); saveEntryQueue.push({ tenantId, entry, trx });
@@ -60,8 +57,8 @@ export class LedgerEntriesStorage {
/** /**
* Saves the ledger entry to the account transactions repository. * Saves the ledger entry to the account transactions repository.
* @param {number} tenantId * @param {number} tenantId
* @param {ILedgerEntry} entry * @param {ILedgerEntry} entry
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
private saveEntry = async ( private saveEntry = async (

View File

@@ -14,8 +14,6 @@ export class AccountTransformer extends Transformer {
*/ */
public includeAttributes = (): string[] => { public includeAttributes = (): string[] => {
return [ return [
'accountTypeLabel',
'accountNormalFormatted',
'formattedAmount', 'formattedAmount',
'flattenName', 'flattenName',
'bankBalanceFormatted', 'bankBalanceFormatted',
@@ -86,22 +84,6 @@ export class AccountTransformer extends Transformer {
return account.plaidItem?.isPaused || false; return account.plaidItem?.isPaused || false;
}; };
/**
* Retrieves formatted account type label.
* @returns {string}
*/
protected accountTypeLabel = (account: any): string => {
return this.context.i18n.__(account.accountTypeLabel);
};
/**
* Retrieves formatted account normal.
* @returns {string}
*/
protected accountNormalFormatted = (account: any): string => {
return this.context.i18n.__(account.accountNormalFormatted);
};
/** /**
* Transformes the accounts collection to flat or nested array. * Transformes the accounts collection to flat or nested array.
* @param {IAccount[]} * @param {IAccount[]}

View File

@@ -1,9 +1,8 @@
import { Service, Inject } from 'typedi'; import { Service, Inject } from 'typedi';
import I18nService from '@/services/I18n/I18nService';
import HasTenancyService from '@/services/Tenancy/TenancyService'; import HasTenancyService from '@/services/Tenancy/TenancyService';
import { AccountTransformer } from './AccountTransform'; import { AccountTransformer } from './AccountTransform';
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import events from '@/subscribers/events';
@Service() @Service()
export class GetAccount { export class GetAccount {
@@ -11,10 +10,10 @@ export class GetAccount {
private tenancy: HasTenancyService; private tenancy: HasTenancyService;
@Inject() @Inject()
private transformer: TransformerInjectable; private i18nService: I18nService;
@Inject() @Inject()
private eventPublisher: EventPublisher; private transformer: TransformerInjectable;
/** /**
* Retrieve the given account details. * Retrieve the given account details.
@@ -40,11 +39,10 @@ export class GetAccount {
new AccountTransformer(), new AccountTransformer(),
{ accountsGraph } { accountsGraph }
); );
const eventPayload = { tenantId, accountId }; return this.i18nService.i18nApply(
[['accountTypeLabel'], ['accountNormalFormatted']],
// Triggers `onAccountViewed` event. transformed,
await this.eventPublisher.emitAsync(events.accounts.onViewed, eventPayload); tenantId
);
return transformed;
}; };
} }

View File

@@ -1,16 +1,17 @@
import { NextFunction, Request, Response } from 'express';
import multer from 'multer'; import multer from 'multer';
import type { Multer } from 'multer'; import type { Multer } from 'multer';
import multerS3 from 'multer-s3'; import multerS3 from 'multer-s3';
import { s3 } from '@/lib/S3/S3'; import { s3 } from '@/lib/S3/S3';
import { Service } from 'typedi'; import { Service } from 'typedi';
import config from '@/config'; import config from '@/config';
import { NextFunction, Request, Response } from 'express';
@Service() @Service()
export class AttachmentUploadPipeline { export class AttachmentUploadPipeline {
/** /**
* Middleware to ensure that S3 configuration is properly set before proceeding. * Middleware to ensure that S3 configuration is properly set before proceeding.
* This function checks if the necessary S3 configuration keys are present and throws an error if any are missing. * This function checks if the necessary S3 configuration keys are present and throws an error if any are missing.
*
* @param req The HTTP request object. * @param req The HTTP request object.
* @param res The HTTP response object. * @param res The HTTP response object.
* @param next The callback to pass control to the next middleware function. * @param next The callback to pass control to the next middleware function.
@@ -48,11 +49,6 @@ export class AttachmentUploadPipeline {
key: function (req, file, cb) { key: function (req, file, cb) {
cb(null, Date.now().toString()); cb(null, Date.now().toString());
}, },
acl: function(req, file, cb) {
// Conditionally set file to public or private based on isPublic flag
const aclValue = true ? 'public-read' : 'private';
cb(null, aclValue); // Set ACL based on the isPublic flag
}
}), }),
}); });
} }

View File

@@ -3,7 +3,7 @@ import { isEmpty } from 'lodash';
import { import {
ISaleInvoiceCreatedPayload, ISaleInvoiceCreatedPayload,
ISaleInvoiceCreatingPaylaod, ISaleInvoiceCreatingPaylaod,
ISaleInvoiceDeletingPayload, ISaleInvoiceDeletePayload,
ISaleInvoiceEditedPayload, ISaleInvoiceEditedPayload,
} from '@/interfaces'; } from '@/interfaces';
import events from '@/subscribers/events'; import events from '@/subscribers/events';
@@ -146,13 +146,13 @@ export class AttachmentsOnSaleInvoiceCreated {
*/ */
private async handleUnlinkAttachmentsOnInvoiceDeleted({ private async handleUnlinkAttachmentsOnInvoiceDeleted({
tenantId, tenantId,
oldSaleInvoice, saleInvoice,
trx, trx,
}: ISaleInvoiceDeletingPayload) { }: ISaleInvoiceDeletePayload) {
await this.unlinkAttachmentService.unlinkAllModelKeys( await this.unlinkAttachmentService.unlinkAllModelKeys(
tenantId, tenantId,
'SaleInvoice', 'SaleInvoice',
oldSaleInvoice.id, saleInvoice.id,
trx trx
); );
} }

View File

@@ -1,9 +0,0 @@
import path from 'path';
import config from '@/config';
export const getUploadedObjectUri = (objectKey: string) => {
return new URL(
path.join(config.s3.bucket, objectKey),
config.s3.endpoint
).toString();
};

View File

@@ -4,27 +4,11 @@ import {
Institution as PlaidInstitution, Institution as PlaidInstitution,
AccountBase as PlaidAccount, AccountBase as PlaidAccount,
TransactionBase as PlaidTransactionBase, TransactionBase as PlaidTransactionBase,
AccountType as PlaidAccountType,
} from 'plaid'; } from 'plaid';
import { import {
CreateUncategorizedTransactionDTO, CreateUncategorizedTransactionDTO,
IAccountCreateDTO, IAccountCreateDTO,
} from '@/interfaces'; } from '@/interfaces';
import { ACCOUNT_TYPE } from '@/data/AccountTypes';
/**
* Retrieves the system account type from the given Plaid account type.
* @param {PlaidAccountType} plaidAccountType
* @returns {string}
*/
const getAccountTypeFromPlaidAccountType = (
plaidAccountType: PlaidAccountType
) => {
if (plaidAccountType === PlaidAccountType.Credit) {
return ACCOUNT_TYPE.CREDIT_CARD;
}
return ACCOUNT_TYPE.BANK;
};
/** /**
* Transformes the Plaid account to create cashflow account DTO. * Transformes the Plaid account to create cashflow account DTO.
@@ -44,7 +28,7 @@ export const transformPlaidAccountToCreateAccount = R.curry(
code: '', code: '',
description: plaidAccount.official_name, description: plaidAccount.official_name,
currencyCode: plaidAccount.balances.iso_currency_code, currencyCode: plaidAccount.balances.iso_currency_code,
accountType: getAccountTypeFromPlaidAccountType(plaidAccount.type), accountType: 'cash',
active: true, active: true,
bankBalance: plaidAccount.balances.current, bankBalance: plaidAccount.balances.current,
accountMask: plaidAccount.mask, accountMask: plaidAccount.mask,

View File

@@ -1,7 +1,14 @@
import { Inject, Service } from 'typedi'; import { Inject, Service } from 'typedi';
import { Knex } from 'knex'; import { Knex } from 'knex';
import { ILedgerEntry, ICashflowTransaction } from '../../interfaces'; import {
import { transformCashflowTransactionType } from './utils'; ILedgerEntry,
ICashflowTransaction,
AccountNormal,
} from '../../interfaces';
import {
transformCashflowTransactionType,
getCashflowAccountTransactionsTypes,
} from './utils';
import LedgerStorageService from '@/services/Accounting/LedgerStorageService'; import LedgerStorageService from '@/services/Accounting/LedgerStorageService';
import Ledger from '@/services/Accounting/Ledger'; import Ledger from '@/services/Accounting/Ledger';
import HasTenancyService from '@/services/Tenancy/TenancyService'; import HasTenancyService from '@/services/Tenancy/TenancyService';
@@ -63,7 +70,7 @@ export default class CashflowTransactionJournalEntries {
debit: cashflowTransaction.isCashDebit debit: cashflowTransaction.isCashDebit
? cashflowTransaction.localAmount ? cashflowTransaction.localAmount
: 0, : 0,
accountNormal: cashflowTransaction?.cashflowAccount?.accountNormal, accountNormal: AccountNormal.DEBIT,
index: 1, index: 1,
}; };
}; };
@@ -136,7 +143,6 @@ export default class CashflowTransactionJournalEntries {
// Retrieves the cashflow transactions with associated entries. // Retrieves the cashflow transactions with associated entries.
const transaction = await CashflowTransaction.query(trx) const transaction = await CashflowTransaction.query(trx)
.findById(cashflowTransactionId) .findById(cashflowTransactionId)
.withGraphFetched('cashflowAccount')
.withGraphFetched('creditAccount'); .withGraphFetched('creditAccount');
// Retrieves the cashflow transaction ledger. // Retrieves the cashflow transaction ledger.

View File

@@ -4,7 +4,6 @@ import { CashflowAccountTransformer } from './CashflowAccountTransformer';
import TenancyService from '@/services/Tenancy/TenancyService'; import TenancyService from '@/services/Tenancy/TenancyService';
import DynamicListingService from '@/services/DynamicListing/DynamicListService'; import DynamicListingService from '@/services/DynamicListing/DynamicListService';
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
import { ACCOUNT_TYPE } from '@/data/AccountTypes';
@Service() @Service()
export default class GetCashflowAccountsService { export default class GetCashflowAccountsService {
@@ -42,20 +41,14 @@ export default class GetCashflowAccountsService {
const accounts = await CashflowAccount.query().onBuild((builder) => { const accounts = await CashflowAccount.query().onBuild((builder) => {
dynamicList.buildQuery()(builder); dynamicList.buildQuery()(builder);
builder.whereIn('account_type', [ builder.whereIn('account_type', ['bank', 'cash']);
ACCOUNT_TYPE.BANK,
ACCOUNT_TYPE.CASH,
ACCOUNT_TYPE.CREDIT_CARD,
]);
builder.modify('inactiveMode', filter.inactiveMode); builder.modify('inactiveMode', filter.inactiveMode);
}); });
// Retrieves the transformed accounts. // Retrieves the transformed accounts.
const transformed = await this.transformer.transform( return this.transformer.transform(
tenantId, tenantId,
accounts, accounts,
new CashflowAccountTransformer() new CashflowAccountTransformer()
); );
return transformed;
} }
} }

View File

@@ -12,7 +12,7 @@ export class GetCashflowTransactionService {
private tenancy: HasTenancyService; private tenancy: HasTenancyService;
@Inject() @Inject()
private transformer: TransformerInjectable; private transfromer: TransformerInjectable;
/** /**
* Retrieve the given cashflow transaction. * Retrieve the given cashflow transaction.
@@ -37,7 +37,7 @@ export class GetCashflowTransactionService {
this.throwErrorCashflowTranscationNotFound(cashflowTransaction); this.throwErrorCashflowTranscationNotFound(cashflowTransaction);
// Transformes the cashflow transaction model to POJO. // Transformes the cashflow transaction model to POJO.
return this.transformer.transform( return this.transfromer.transform(
tenantId, tenantId,
cashflowTransaction, cashflowTransaction,
new CashflowTransactionTransformer() new CashflowTransactionTransformer()

View File

@@ -122,7 +122,6 @@ export default class NewCashflowTransactionService {
* @param {number} tenantId - * @param {number} tenantId -
* @param {ICashflowOwnerContributionDTO} ownerContributionDTO * @param {ICashflowOwnerContributionDTO} ownerContributionDTO
* @param {number} userId - User id. * @param {number} userId - User id.
* @returns {Promise<ICashflowTransaction>}
*/ */
public newCashflowTransaction = async ( public newCashflowTransaction = async (
tenantId: number, tenantId: number,

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