Compare commits

...

274 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
Ahmed Bouhuolia
9f21f649f0 Merge pull request #662 from bigcapitalhq/refactor-expense-gl
refactor: The expense G/L writer
2024-09-04 21:10:51 +02:00
Ahmed Bouhuolia
d3d2112b8a refactor: The expense G/L writer 2024-09-04 21:10:13 +02:00
Ahmed Bouhuolia
3795322a65 Merge pull request #659 from bigcapitalhq/format-money-table-columns
feat: Tabular number of all money columns
2024-09-04 18:48:38 +02:00
Ahmed Bouhuolia
fe5cd5a8ea feat: Mark more columns as money columns 2024-09-04 18:46:42 +02:00
Ahmed Bouhuolia
c032a5db16 Merge pull request #661 from bigcapitalhq/fix-payment-made-full-amount
fix: Payment made filling the form full amount field
2024-09-04 17:35:54 +02:00
Ahmed Bouhuolia
3fcb6fefde fix: Payment made filling the form full amount field 2024-09-04 17:34:19 +02:00
Ahmed Bouhuolia
16f5cb713d Merge pull request #660 from bigcapitalhq/fix-cast-array-rule-ids
fix: Array cast of recognize function rule ids
2024-09-04 16:49:32 +02:00
Ahmed Bouhuolia
f7a7925028 fix: Array cast of recognize function rule ids 2024-09-04 16:48:34 +02:00
Ahmed Bouhuolia
66fb0c9fa3 feat: Tabular number of all money columns 2024-09-04 14:58:31 +02:00
Ahmed Bouhuolia
85acc85f17 Merge pull request #655 from bigcapitalhq/ui-tweaks
feat: Datatable UI improvements
2024-09-04 14:02:43 +02:00
Ahmed Bouhuolia
c76ce09191 feat: optimize the style of bank account transactions tables 2024-09-04 13:59:13 +02:00
Ahmed Bouhuolia
e3532098b2 Merge branch 'develop' into ui-tweaks 2024-09-04 09:53:55 +02:00
Ahmed Bouhuolia
b6783eb22e Merge pull request #658 from bigcapitalhq/banking-layout-breaking
feat: Bank pages layout breaking
2024-09-04 09:52:35 +02:00
Ahmed Bouhuolia
4ef00ab122 feat: Bank pages layout breaking 2024-09-04 09:51:35 +02:00
Ahmed Bouhuolia
3dd827417a fix: remove duplicated event types 2024-09-03 17:31:00 +02:00
Ahmed Bouhuolia
cbacd02aa2 Merge pull request #656 from bigcapitalhq/add-help-dropdown-menu
feat: Add help dropdown menu
2024-09-03 17:28:12 +02:00
Ahmed Bouhuolia
3b7e0fb78a Merge pull request #657 from bigcapitalhq/suspense-lazy-banking-pages
fix: Suspense the lazy loaded components in banking pages
2024-09-03 17:26:02 +02:00
Ahmed Bouhuolia
9add716395 fix: Suspense the lazy loaded components in banking pages 2024-09-03 17:24:11 +02:00
Ahmed Bouhuolia
083ea28a1f feat: Add help dropdown menu 2024-09-03 16:41:20 +02:00
Ahmed Bouhuolia
1b51742c36 feat: Datatable UI improvements 2024-09-03 16:39:13 +02:00
Ahmed Bouhuolia
0c6f23e770 Merge pull request #654 from bigcapitalhq/expense-credit-card
fix: Expense cannot accept credit card as payment account
2024-09-03 12:23:03 +02:00
Ahmed Bouhuolia
37a8ca4e97 fix: Expense cannot accept credit card as payment account 2024-09-03 12:22:07 +02:00
Ahmed Bouhuolia
795303c3a8 Merge pull request #653 from bigcapitalhq/tracking-more-events
feat: Tracking more Posthog events
2024-09-03 11:28:27 +02:00
Ahmed Bouhuolia
63ba3f0898 Merge branch 'develop' into tracking-more-events 2024-09-03 11:28:05 +02:00
Ahmed Bouhuolia
62594efa00 feat: Tracking more Posthog events 2024-09-03 11:26:24 +02:00
Ahmed Bouhuolia
9ac0dcbdc3 Merge pull request #651 from bigcapitalhq/bank-transactions-events
feat: Track banking service events
2024-09-02 17:13:49 +02:00
Ahmed Bouhuolia
df588dc4ce Merge branch 'develop' into bank-transactions-events 2024-09-02 17:13:43 +02:00
Ahmed Bouhuolia
d54f14a87a feat: Track banking service events 2024-09-02 17:12:37 +02:00
Ahmed Bouhuolia
a4d4be54c1 Merge pull request #650 from bigcapitalhq/cover-more-tracking-events
feat: Cover more tracking events.
2024-09-02 15:20:11 +02:00
Ahmed Bouhuolia
ddd17e74b5 feat: Cover more tracking events. 2024-09-02 15:19:01 +02:00
Ahmed Bouhuolia
81c0761fbe Merge pull request #649 from bigcapitalhq/import-multi-branches-expenses
fix: Integrate multiple branches with expense resource
2024-09-02 15:02:53 +02:00
Ahmed Bouhuolia
0812e3087e fix: Integrate multiple branches with expense resource 2024-09-02 15:02:02 +02:00
Ahmed Bouhuolia
0dd05493b2 Merge pull request #645 from bigcapitalhq/multi-branches-warehoues-in-importing
feat: integrate multiple branches and warehouses to resource importing
2024-09-02 14:46:08 +02:00
Ahmed Bouhuolia
bfb3909d26 feat: integrate multiple branches and warehouses with import 2024-09-02 14:42:05 +02:00
Ahmed Bouhuolia
266902026e Merge pull request #648 from bigcapitalhq/fix-bank-transactions-infinity-scrolling
fix: Bank transactions infinity scrolling
2024-09-02 10:41:54 +02:00
Ahmed Bouhuolia
791c4a4e9e fix: Bank transactions infinity scrolling 2024-09-02 10:41:16 +02:00
Ahmed Bouhuolia
2cfa9123b8 fix: Syntax error 2024-09-01 23:32:54 +02:00
Ahmed Bouhuolia
bb9614aafb Merge pull request #646 from bigcapitalhq/events-tracking
feat(server): Events tracking using Posthog
2024-09-01 23:06:38 +02:00
Ahmed Bouhuolia
302564a56e fix: remove the un-used file 2024-09-01 23:02:41 +02:00
Ahmed Bouhuolia
dcfc231d4d feat(server): Events tracking using Posthog 2024-09-01 23:01:25 +02:00
Ahmed Bouhuolia
64080ed678 Merge pull request #644 from bigcapitalhq/set-default-index-entries
fix: Set default index to transaction entries
2024-09-01 15:10:40 +02:00
Ahmed Bouhuolia
263935d91e Merge pull request #643 from bigcapitalhq/import-bugs
fix: Import bugs
2024-09-01 15:02:31 +02:00
Ahmed Bouhuolia
7f5ffb8da1 fix: Add the missing columns to the payment received and made models 2024-09-01 15:00:51 +02:00
Ahmed Bouhuolia
f07d25edbe fix: Set default index to transaction entries 2024-09-01 13:32:47 +02:00
Ahmed Bouhuolia
7c07d6b5ff fix: The unimported functions 2024-09-01 11:16:50 +02:00
Ahmed Bouhuolia
e433f4ad68 fix: Item resource columns labels 2024-09-01 11:07:55 +02:00
Ahmed Bouhuolia
a79b9caff6 Merge pull request #641 from bigcapitalhq/fix-getting-sheet-columns
fix: Getting the sheet columns in import sheet
2024-08-30 17:56:53 +02:00
Ahmed Bouhuolia
2227cead66 Merge pull request #624 from bigcapitalhq/subscription-middleware
fix: Subscription middleware
2024-08-30 17:56:02 +02:00
Ahmed Bouhuolia
410c4ea3e2 fix: Subscription active detarminer 2024-08-30 17:52:53 +02:00
Ahmed Bouhuolia
f92acbcbe0 fix: Getting the sheet columns in import sheet 2024-08-30 17:03:16 +02:00
Ahmed Bouhuolia
c986585cd9 Merge pull request #640 from bigcapitalhq/fix-typo-one-click-demo
fix: Typo one-click demo page
2024-08-30 00:15:29 +02:00
Ahmed Bouhuolia
250f0a30ef fix: Typo one-click demo page 2024-08-30 00:14:59 +02:00
Ahmed Bouhuolia
84b5e1adc1 Merge pull request #639 from bigcapitalhq/add-customer-type-to-customers
fix: Add customer type to customers resource
2024-08-29 22:50:20 +02:00
Ahmed Bouhuolia
2ab28370db fix: Add customer type to customers resource 2024-08-29 22:49:49 +02:00
Ahmed Bouhuolia
a88a525326 Merge pull request #638 from bigcapitalhq/use-standard-date-format-export
fix: use standard ISO 8601 format for exported data
2024-08-29 22:40:59 +02:00
Ahmed Bouhuolia
fce8e2c5a4 fix: use standard ISO 8601 format for exported data 2024-08-29 22:39:51 +02:00
Ahmed Bouhuolia
095608266c Merge pull request #632 from bigcapitalhq/split-lazy-loading
feat: Optimize loading perf. by splitting big chunks and lazy loading them
2024-08-29 21:27:40 +02:00
Ahmed Bouhuolia
0ec8aaa330 fix: delete unwanted files 2024-08-29 21:27:20 +02:00
Ahmed Bouhuolia
9fcb3ef77d feat: Add fallback spinner to authentication lazy-loaded pages 2024-08-29 21:19:36 +02:00
Ahmed Bouhuolia
dc61c57daf fix: Add spinner to preferences lazy loaded pages 2024-08-29 21:03:27 +02:00
Ahmed Bouhuolia
af284f3f6d feat: split the preferences pages 2024-08-29 20:49:08 +02:00
Ahmed Bouhuolia
c43123db76 Merge pull request #636 from bigcapitalhq/expand-export-page-size
fix: Expand the resources export page size limitation
2024-08-29 14:23:48 +02:00
Ahmed Bouhuolia
ebbcab3926 fix: Expand the resources export page size limitation 2024-08-29 14:22:45 +02:00
Ahmed Bouhuolia
a235f573c0 Merge pull request #635 from bigcapitalhq/fix-avoid-cost-job-import-preview
fix: Avoid running the cost job in import preview
2024-08-29 10:09:13 +02:00
Ahmed Bouhuolia
84a0b8f495 fix: re-schedule the jobs have date from the current moment 2024-08-29 10:05:38 +02:00
Ahmed Bouhuolia
b87321c897 fix: Avoid running the cost job in import preview 2024-08-28 22:15:15 +02:00
Ahmed Bouhuolia
fc6ebfea5c Debounce scheduling calculating items cost 2024-08-28 21:25:47 +02:00
Ahmed Bouhuolia
c9fe6d9b37 feat: Optimize loading perf. by spliting big chunks and lazy loading them 2024-08-26 22:51:40 +02:00
Ahmed Bouhuolia
161d60393a Merge pull request #629 from bigcapitalhq/details-subscription
fix: Add subscription plans offer text
2024-08-25 19:44:34 +02:00
Ahmed Bouhuolia
79413fa85e fix: Add subscription plans offer text 2024-08-25 19:43:54 +02:00
Ahmed Bouhuolia
58552c6c94 Merge pull request #628 from bigcapitalhq/fix-webapp-env-variables
fix: Make webapp package env variables dynamic
2024-08-25 18:21:55 +02:00
Ahmed Bouhuolia
2072e35cfa fix: Make webapp package env variables dynamic 2024-08-25 18:21:08 +02:00
Ahmed Bouhuolia
1eaac9d691 Merge remote-tracking branch 'refs/remotes/origin/develop' into develop 2024-08-25 14:18:04 +02:00
Ahmed Bouhuolia
a916e8a0cb fix: syntax error 2024-08-25 14:17:23 +02:00
Ahmed Bouhuolia
a56f560036 Merge pull request #627 from bigcapitalhq/fix-style-tweeks-in-onboarding-page
fix: Style tweaks in onboarding page
2024-08-25 13:20:39 +02:00
Ahmed Bouhuolia
0fb886936c Merge pull request #626 from bigcapitalhq/disable-sms-service
fix: Disable sms service until Twilo integration
2024-08-25 13:20:14 +02:00
Ahmed Bouhuolia
670136916f fix: Style tweaks in onboarding page 2024-08-25 13:19:16 +02:00
Ahmed Bouhuolia
768297f137 fix: Disable sms service until Twilo integration 2024-08-25 13:07:07 +02:00
Ahmed Bouhuolia
ef505a0a62 Merge pull request #625 from bigcapitalhq/manual-journal-number-prefix
fix: Add prefix J-00001 to manual journals increments
2024-08-25 13:05:52 +02:00
Ahmed Bouhuolia
42d40620ec fix: add prefix J-00001 to manual journals increments 2024-08-25 13:05:28 +02:00
Ahmed Bouhuolia
ee2d8d3065 fix: subscription middleare 2024-08-25 12:42:42 +02:00
Ahmed Bouhuolia
959ef7a691 Merge branch 'listen-payment-webhooks' into develop 2024-08-24 21:52:04 +02:00
Ahmed Bouhuolia
60f03f534b fix(subscription): event TS types 2024-08-24 21:51:15 +02:00
Ahmed Bouhuolia
e44ebb700a Merge pull request #623 from bigcapitalhq/listen-payment-webhooks
fix: Listen to payment webhooks
2024-08-24 21:49:36 +02:00
Ahmed Bouhuolia
8e94c7a755 feat(subscription): invalidate subscription cache 2024-08-24 21:40:28 +02:00
Ahmed Bouhuolia
3a2ca36c07 Merge branch 'develop' into listen-payment-webhooks 2024-08-24 21:08:47 +02:00
Ahmed Bouhuolia
88ece74c8a Merge pull request #622 from wolone/develop
Fix: Syntax error caused error
2024-08-24 20:48:14 +02:00
Ahmed Bouhuolia
67a8610328 feat: cancel/resume LS subscriptions 2024-08-24 20:46:30 +02:00
Ahmed Bouhuolia
278d61ce61 fix: Listen to payment webhooks 2024-08-24 18:50:12 +02:00
wolone
e72d6ad6b8 Fix: Syntax error caused error 2024-08-24 23:33:53 +08:00
Ahmed Bouhuolia
bf66b31679 Merge pull request #590 from bigcapitalhq/filter-uncategorized-bank-transactions
feat(banking): Filter uncategorized bank transactions by date
2024-08-23 17:42:59 +02:00
Ahmed Bouhuolia
b4d426d2e8 feat: Filter uncategorized transactions by date 2024-08-23 17:40:05 +02:00
Ahmed Bouhuolia
7f7dd270e7 Merge pull request #619 from bigcapitalhq/change-banking-service-language
feat: change banking service language
2024-08-23 02:00:47 +02:00
Ahmed Bouhuolia
f6bad8fe30 fix: wip filter uncategorized transactions by date 2024-08-23 01:57:52 +02:00
Ahmed Bouhuolia
820b363f79 feat: change banking service language 2024-08-22 20:49:15 +02:00
Ahmed Bouhuolia
07740a51ef Merge pull request #616 from bigcapitalhq/one-click-demo-account
feat(ee): One-click demo account
2024-08-22 20:33:06 +02:00
Ahmed Bouhuolia
d15fb6fe19 chore: document http query 2024-08-22 20:32:48 +02:00
Ahmed Bouhuolia
5749ccec81 feat: seed more demo bank transactions 2024-08-22 19:50:31 +02:00
Ahmed Bouhuolia
4a99f6c0cf feat: one-click demo account 2024-08-22 19:21:23 +02:00
Ahmed Bouhuolia
59f480f9d5 feat: add more demo account seeders 2024-08-22 13:04:51 +02:00
Ahmed Bouhuolia
6cb9c919b5 Merge branch 'develop' into one-click-demo-account 2024-08-22 10:48:24 +02:00
Ahmed Bouhuolia
1062b65b5b feat: wip bank account transactions date filter 2024-08-22 01:03:03 +02:00
Ahmed Bouhuolia
bf3a70cabd Merge branch 'develop' into filter-uncategorized-bank-transactions 2024-08-22 00:12:37 +02:00
Ahmed Bouhuolia
f46cd28f87 Merge pull request #618 from bigcapitalhq/display-details-bank-account
fix: Some bank account details hidden
2024-08-21 21:22:15 +02:00
Ahmed Bouhuolia
dffcfe50aa fix: some bank account details hidden 2024-08-21 21:19:59 +02:00
Ahmed Bouhuolia
fac55efbc7 Merge pull request #617 from bigcapitalhq/fix-impoort-itemns
fix: Cannot import items income and cost accounts
2024-08-21 19:33:50 +02:00
Ahmed Bouhuolia
8b90ce5f6c fix: cannot import items income and cost accounts 2024-08-21 19:32:59 +02:00
Ahmed Bouhuolia
705b8da053 fix: protect the one-click demo accounts endpoints 2024-08-21 01:04:18 +02:00
Ahmed Bouhuolia
4a05ccc692 feat: add more seedders 2024-08-20 23:40:23 +02:00
Ahmed Bouhuolia
3200d65d90 feat: add demo account button on onboarding 2024-08-20 22:01:36 +02:00
Ahmed Bouhuolia
3f23038227 chore: comment 2024-08-20 18:49:39 +02:00
Ahmed Bouhuolia
408c807fc2 feat: import sheet files on initializing demo account 2024-08-20 18:30:21 +02:00
Ahmed Bouhuolia
d29079a8c5 fix: one-click demo account 2024-08-20 12:51:23 +02:00
Ahmed Bouhuolia
cca596b4a9 fix: one click demo 2024-08-19 21:21:39 +02:00
Ahmed Bouhuolia
b768f18294 chore: change subscription prices 2024-08-19 13:08:09 +02:00
Ahmed Bouhuolia
fed620505d feat: add one click endpoint 2024-08-19 12:10:38 +02:00
Ahmed Bouhuolia
a008aea3f3 feat(ee): one-click demo account 2024-08-19 12:08:58 +02:00
Ahmed Bouhuolia
25297bc191 chore: dump CHANGELOG 2024-08-18 20:56:10 +02:00
Ahmed Bouhuolia
1989887b25 Merge pull request #615 from bigcapitalhq/activate-account-from-drawer
feat: activate/inactivate account from drawer details
2024-08-18 20:24:07 +02:00
Ahmed Bouhuolia
e4fb126d39 feat: activate/inactivate account from drawer details 2024-08-18 20:23:47 +02:00
Ahmed Bouhuolia
5fcb2d9cc9 Merge pull request #614 from bigcapitalhq/delete-bank-account-with-uncategorized-transactions
fix: Delete bank account with uncategorized transactions
2024-08-18 19:55:15 +02:00
Ahmed Bouhuolia
06ea631732 fix: making pagination more readable 2024-08-18 19:38:15 +02:00
Ahmed Bouhuolia
2f21107a43 feat: delete uncategorized transactions before deleting bank account 2024-08-18 19:30:09 +02:00
Ahmed Bouhuolia
fb8118bea8 fix: Delete bank account with uncategorized transactions 2024-08-18 14:20:23 +02:00
Ahmed Bouhuolia
4ba1c0aa22 Merge pull request #612 from Champetaman/fix-manual-journal-date-expense-drawer
Fix: Correctly display Date, Published At, and Created At in ExpenseDrawerHeader
2024-08-18 11:20:25 +02:00
Ahmed Bouhuolia
169f115fa0 fix: remove the default value from date and createdAt because always required 2024-08-18 11:19:06 +02:00
Ahmed Bouhuolia
61ab2b78d9 Merge pull request #613 from bigcapitalhq/language-typos
fix: Language typos
2024-08-18 11:12:49 +02:00
Ahmed Bouhuolia
93732430fc fix: Language typos 2024-08-18 11:11:17 +02:00
Camilo Oviedo
0215206220 add: Created attribute formattedPublishedAt to display on Expense Drawer 2024-08-17 10:11:05 +10:00
Camilo Oviedo
3c8956fedf fix: Correctly display Date, Published At, and Created At fields 2024-08-17 10:09:44 +10:00
Ahmed Bouhuolia
4477ada1ad Merge pull request #611 from bigcapitalhq/fix-connection-lost
fix: Database connection lost error
2024-08-15 23:48:28 +02:00
Ahmed Bouhuolia
fde9ccc5ca fix: Database connection lost error 2024-08-15 23:47:21 +02:00
Ahmed Bouhuolia
bbbd96f159 Merge pull request #604 from bigcapitalhq/inconsistance-pagination-page-size
fix: inconsistance page size of paginated data tables
2024-08-14 22:14:03 +02:00
Ahmed Bouhuolia
9f4de8115f fix: initial page size to the data tables from store state 2024-08-14 22:12:21 +02:00
Ahmed Bouhuolia
d9f241a2f8 Merge branch 'develop' into inconsistance-pagination-page-size 2024-08-14 22:01:04 +02:00
Ahmed Bouhuolia
ee96dc68cc fix: Change Dropzone title and subtitle (#607) 2024-08-14 20:13:45 +02:00
Ahmed Bouhuolia
b12f090d13 fix: matching bank transactions should create associate payment transactions for bills and invoicese. (#606) 2024-08-14 19:23:15 +02:00
allcontributors[bot]
f6ce761a27 docs: add Champetaman as a contributor for code (#605)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2024-08-14 16:20:50 +02:00
Ahmed Bouhuolia
8c89e04f54 Merge pull request #603 from Champetaman/fix-import-drag-area-colors-blue
Fix: Enhance Dropzone visual feedback for dropzone
2024-08-14 16:17:44 +02:00
Ahmed Bouhuolia
f97b127a69 fix: style tweaks in dropzoen accept/reject modes 2024-08-14 16:12:13 +02:00
Ahmed Bouhuolia
5c1fa8f5cd fix: inconsistance page size of paginated data tables 2024-08-14 15:23:14 +02:00
Ahmed Bouhuolia
64c4d7b5a4 Merge pull request #599 from bigcapitalhq/rename-payment-receives-to-payment-received
fix: Typo payment receive messages
2024-08-14 14:54:54 +02:00
Ahmed Bouhuolia
ea2fad648b Merge branch 'develop' into rename-payment-receives-to-payment-received 2024-08-14 14:54:48 +02:00
Camilo Oviedo
9b8b51cb91 fix: Enhance visual feedback on file drag-and-drop 2024-08-14 16:29:38 +10:00
Camilo Oviedo
09b7e74d65 fix: Enhance visual feedback on file drag-and-drop 2024-08-14 16:21:48 +10:00
Ahmed Bouhuolia
7137e06d99 Merge pull request #602 from bigcapitalhq/remove-views-tabs-from-receipts-list
fix: Remove views tabs from receipts list
2024-08-14 00:06:14 +02:00
Ahmed Bouhuolia
fc29b765f7 fix: remove views tabs from receipts list 2024-08-14 00:04:11 +02:00
Ahmed Bouhuolia
2946475f89 Merge pull request #601 from bigcapitalhq/autofill-quick-customer-vendor
fix: Autofill the quick created customer/vendor
2024-08-13 23:59:26 +02:00
Ahmed Bouhuolia
d2193fdac0 fix: autofill the quick created customer/vendor 2024-08-13 23:55:53 +02:00
Ahmed Bouhuolia
ec2b7e332e Merge pull request #600 from bigcapitalhq/fix-typo-categories-list
fix: Typo categories list
2024-08-13 19:39:17 +02:00
Ahmed Bouhuolia
a3704df6dd fix: Typo categories list 2024-08-13 19:38:07 +02:00
Ahmed Bouhuolia
c3c784e52c Merge pull request #598 from bigcapitalhq/move-payment-mades-to-payments-made
fix: Typo payments made
2024-08-13 19:33:02 +02:00
Ahmed Bouhuolia
bbcf695a6c fix: Typo payments made 2024-08-13 19:10:09 +02:00
Ahmed Bouhuolia
038d4dd5a7 chore: renmame payment receive term to payment received 2024-08-13 15:15:07 +02:00
Ahmed Bouhuolia
961e4b99e8 fix: rename interfaces to PaymentReceived 2024-08-13 14:17:37 +02:00
Ahmed Bouhuolia
9991eebaaf fix(server): rename term to 2024-08-13 13:41:09 +02:00
Ahmed Bouhuolia
cd90fede54 Merge pull request #597 from bigcapitalhq/refresh-acccounts-bank-transactions
fix: Refresh accounts and account transactions.
2024-08-13 11:39:11 +02:00
Ahmed Bouhuolia
a2d28648bd fix: refresh accounts and account transactions. 2024-08-13 11:37:59 +02:00
Ahmed Bouhuolia
3097d05eda Merge pull request #596 from bigcapitalhq/transaction-type-description-general-ledger
fix: Transaction type and description do not show in general ledger.
2024-08-12 21:11:02 +02:00
Ahmed Bouhuolia
ff94d8d9b2 fix: Transaction type and description do not show in general ledger. 2024-08-12 21:08:02 +02:00
Ahmed Bouhuolia
79cc09fad9 Merge pull request #595 from bigcapitalhq/add-comparators-to-amount-bank-rule
feat: Add amount comparators to amount bank rule field
2024-08-12 20:17:20 +02:00
Ahmed Bouhuolia
c1b29c3f23 fix: add equal condition to number fields on bank rule 2024-08-12 20:16:18 +02:00
Ahmed Bouhuolia
cf4bb3007e feat: run re-recognizing bank transactions on edit bank rule 2024-08-12 20:07:01 +02:00
Ahmed Bouhuolia
193a86cf30 feat: add amount comparators to amount bank rule field 2024-08-12 17:53:57 +02:00
Ahmed Bouhuolia
7a81f14eb2 Merge pull request #594 from bigcapitalhq/multi-lines-transactions-statements
fix: Multi-lines transactions statements
2024-08-12 16:33:01 +02:00
Ahmed Bouhuolia
14d1f0bd1d fix: Multi-lines transactions statements 2024-08-12 16:31:36 +02:00
Ahmed Bouhuolia
82f8648c59 Merge pull request #593 from bigcapitalhq/fix-round-pending-matching
fix: Rounding the total amount the pending and matched transactions
2024-08-12 13:01:27 +02:00
Ahmed Bouhuolia
c928940d32 fix: rounding the total amount the pending and matched transactions 2024-08-12 13:01:00 +02:00
Ahmed Bouhuolia
0a78d56015 Merge pull request #592 from bigcapitalhq/matching-reconcile-branches
fix: Should not load branches on reconcile matching form if the branches not enabled
2024-08-12 11:29:48 +02:00
Ahmed Bouhuolia
1a5716873e fix: should not load branches on reconcile matching form if the branches not enabled 2024-08-12 11:28:34 +02:00
Ahmed Bouhuolia
01b7c86ab9 Merge pull request #588 from Champetaman/fix-dev-variable-setting-error
Update `dev` Script in `package.json` to Use `cross-env`
2024-08-12 10:55:45 +02:00
Ahmed Bouhuolia
0ca209b195 Merge pull request #589 from bigcapitalhq/bank-pending-transactions
feat: Pending bank transactions
2024-08-12 10:54:32 +02:00
Ahmed Bouhuolia
be6f6e3c73 fix: function description 2024-08-12 10:54:16 +02:00
Ahmed Bouhuolia
cb016be78c fix: avoid decrement/increment for pending bank transactions 2024-08-12 10:48:36 +02:00
Ahmed Bouhuolia
fc085f2328 Merge pull request #587 from bigcapitalhq/big-244-uncategorize-bank-transactions-in-bulk
feat: Uncategorize bank transactions in bulk
2024-08-12 10:11:55 +02:00
Ahmed Bouhuolia
9a34f3e283 fix: invalidate account cache on bulk uncategorizing 2024-08-12 10:11:40 +02:00
Ahmed Bouhuolia
7054e862d5 feat: pending transactions table 2024-08-11 22:51:58 +02:00
Ahmed Bouhuolia
faa81abee4 feat(banking): uncategorize bank transactions in bulk 2024-08-11 21:26:02 +02:00
Ahmed Bouhuolia
6d01f2a323 Merge pull request #591 from bigcapitalhq/import-export-tax-rates
feat: import and export tax rates
2024-08-11 19:51:50 +02:00
Ahmed Bouhuolia
72678bb936 feat: import and export tax rates 2024-08-11 19:51:16 +02:00
Ahmed Bouhuolia
df8b68fda6 feat(banking): Filter uncategorized bank transactions 2024-08-11 18:34:45 +02:00
Ahmed Bouhuolia
9ae5644af9 feat: Pending bank transactions 2024-08-11 16:14:13 +02:00
Camilo Oviedo
e8830c5911 Fix dev variable setting causing error on windows for craco start command 2024-08-11 22:14:54 +10:00
Camilo Oviedo
7699889bd6 Fix dev variable setting causing error on windows for craco start command 2024-08-11 21:57:50 +10:00
Ahmed Bouhuolia
35a061d188 feat: Uncategorize bank transactions in bulk 2024-08-11 13:02:38 +02:00
Ahmed Bouhuolia
c7c021c969 fix(banking): detarmine if Plaid item is disabled (#585) 2024-08-11 12:10:15 +02:00
Ahmed Bouhuolia
be8352654e Merge pull request #559 from oleynikd/tax-precisions
Increased tax_amount_withheld decimal precision
2024-08-08 16:31:43 +02:00
Ahmed Bouhuolia
fb58ab8cc1 Merge pull request #571 from bigcapitalhq/remove-controller-escape
fix: Remove the request body escape.
2024-08-08 16:12:31 +02:00
Ahmed Bouhuolia
8da89ebe8b fix: remove the request body escape. 2024-08-08 16:10:42 +02:00
Ahmed Bouhuolia
d43d46ebec Merge pull request #570 from bigcapitalhq/popover2-version
fix: Update @blueprintjs/popover2 version
2024-08-08 12:57:48 +02:00
Ahmed Bouhuolia
ac3a514795 fix: update @blueprintjs/popover2 version 2024-08-08 12:57:08 +02:00
Ahmed Bouhuolia
f67c63a4fa Merge pull request #569 from bigcapitalhq/fix-edit-bank-rule-recognized
fix: Recognize transactions on editing bank rule
2024-08-08 00:26:18 +02:00
Ahmed Bouhuolia
0025dcf8d4 fix: add recognize jobs 2024-08-08 00:23:47 +02:00
Ahmed Bouhuolia
81995dc94f fix: recognize transactions on editing bank rule 2024-08-08 00:20:17 +02:00
Ahmed Bouhuolia
3fcc70c1d8 Merge pull request #568 from bigcapitalhq/fix-banking-api-query
fix: Banking API account and page query.
2024-08-07 20:17:49 +02:00
Ahmed Bouhuolia
a986c7a250 fix: Banking api account and page query. 2024-08-07 20:08:59 +02:00
allcontributors[bot]
37e25a8061 docs: add mittalsam98 as a contributor for bug (#567)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2024-08-07 18:58:37 +02:00
Ahmed Bouhuolia
cfba628465 Merge pull request #562 from mittalsam98/fix/wrong-due-amount
fix: the wrong invoice due amouynt.
2024-08-07 18:56:28 +02:00
Ahmed Bouhuolia
3d200f4d7d fix: wrong invoice due amount 2024-08-07 18:52:36 +02:00
Ahmed Bouhuolia
f21b01b1d6 feat: Move billing to preferences (#566)
* feat: move billing to preferences

* chore: remove the commented lines
2024-08-06 12:12:41 +02:00
Ahmed Bouhuolia
3cbcfac333 Merge pull request #565 from bigcapitalhq/fix-edit-bank-rule
fix: Edit bank rule
2024-08-06 00:50:05 +02:00
Ahmed Bouhuolia
cc21e1856f fix: Edit bank rule 2024-08-06 00:48:58 +02:00
Ahmed Bouhuolia
efd0e1e225 Merge pull request #564 from bigcapitalhq/fix-banking-bugs
fix: Banking service bugs
2024-08-05 23:06:03 +02:00
Ahmed Bouhuolia
521b083ed7 fix: retrieve the excluded transactions count 2024-08-05 22:50:58 +02:00
Ahmed Bouhuolia
a09fe26df7 fix: group query key constants in seperate file 2024-08-05 21:36:34 +02:00
Ahmed Bouhuolia
c7a85c4cf8 fix: categorize transactions on recognized transactions table 2024-08-05 21:20:11 +02:00
Ahmed Bouhuolia
f6350d3d61 fix: Should not show the excluded transactions in recognized transactions 2024-08-05 21:11:15 +02:00
Ahmed Bouhuolia
64c0732e5f fix: infinity scrolling of bank account transactions 2024-08-05 20:57:13 +02:00
Ahmed Bouhuolia
8e99a31455 fix: validate exclude and unexclude uncategorized transaction 2024-08-05 15:56:11 +02:00
Ahmed Bouhuolia
6d0d0689e1 Merge pull request #533 from bigcapitalhq/bulk-categorize-bank-transactions
feat: Bulk categorize bank transactions
2024-08-04 22:23:11 +02:00
Ahmed Bouhuolia
9836129e49 Merge branch 'develop' into bulk-categorize-bank-transactions 2024-08-04 22:23:02 +02:00
Ahmed Bouhuolia
86631ea8c3 chore: fix typing 2024-08-04 22:20:31 +02:00
Ahmed Bouhuolia
475ccd4903 Merge pull request #563 from bigcapitalhq/pause-resume-bank-feeds-syncing
feat: pause/resume bank account feeds syncing
2024-08-04 21:47:32 +02:00
Ahmed Bouhuolia
8608144ec1 chore: components description 2024-08-04 21:47:16 +02:00
Ahmed Bouhuolia
f9cf6d325a feat: pause bank account feeds 2024-08-04 21:14:05 +02:00
Ahmed Bouhuolia
fc0240c692 feat: confimation dialog on disconnecting bank account 2024-08-04 19:44:36 +02:00
Ahmed Bouhuolia
b84675325f feat: alert messages of pause.resume bank feeds 2024-08-04 16:05:35 +02:00
Ahmed Bouhuolia
647bed5c67 feat: control the multi-select switch 2024-08-04 15:42:53 +02:00
Ahmed Bouhuolia
00f5bb1d73 fix: decrement uncategorized transactions count 2024-08-04 13:15:20 +02:00
Ahmed Bouhuolia
208800b411 feat: wip pause/resume bank feeds syncing 2024-08-04 11:22:21 +02:00
Ahmed Bouhuolia
5e12a4cea4 feat: pause/resume bank account feeds syncing 2024-08-04 00:36:19 +02:00
Ahmed Bouhuolia
fdf3e34f1c feat: wip uncategorize bank transaction 2024-08-03 23:30:23 +02:00
Ahmed Bouhuolia
d74337fb94 feat: wip multi-select transactions to categorization and matching 2024-08-03 22:01:21 +02:00
Sachin
8cab012324 fix: due Amount on edit page is calculated wrong with "Exclusive of Tax" Invoice mode 2024-08-03 23:56:02 +05:30
Ahmed Bouhuolia
940b4f9175 Merge pull request #553 from oleynikd/attachments
Download attachments (documents) with original filenames
2024-08-02 02:42:52 +02:00
Ahmed Bouhuolia
5d0dd1fe3f Merge branch 'main' into develop 2024-08-01 20:10:01 +02:00
Denis
7147e230de Increased tax_amount_withheld decimal precision
Fixing #547
2024-08-01 16:31:14 +03:00
Ahmed Bouhuolia
5ce11f192f feat: reset the state once closing categorization aside 2024-08-01 14:02:02 +02:00
Ahmed Bouhuolia
71e865e9b7 Merge remote-tracking branch 'refs/remotes/origin/bulk-categorize-bank-transactions' into bulk-categorize-bank-transactions 2024-08-01 13:46:19 +02:00
Ahmed Bouhuolia
590506f183 Merge branch 'develop' into bulk-categorize-bank-transactions 2024-08-01 13:46:03 +02:00
Ahmed Bouhuolia
bed281a637 feat: wip multipe transactions categorization 2024-08-01 13:44:49 +02:00
Ahmed Bouhuolia
47dd767b3a feat: getting matched transactiosn from multi uncategorized transactions 2024-08-01 12:11:54 +02:00
Ahmed Bouhuolia
8623b69991 feat: getting matched transactiosn from multi uncategorized transactions 2024-08-01 12:11:40 +02:00
Denis
a1ddc81dac Fixed double slash in attachments route 2024-07-30 23:54:46 +03:00
Denis
832cdacebf Download attachments with original filenames 2024-07-30 23:48:15 +03:00
Ahmed Bouhuolia
9f979080b6 fix: remove console.log 2024-07-30 21:55:44 +02:00
Ahmed Bouhuolia
7f7301b31e Merge pull request #544 from bigcapitalhq/billing-subscription-page
feat: Billing subscription page
2024-07-30 21:44:55 +02:00
Ahmed Bouhuolia
6affbedef4 feat: description to billing page 2024-07-30 21:43:33 +02:00
Ahmed Bouhuolia
ba7f32c1bf feat: abstract the pricing plans for setup and billing page 2024-07-30 17:47:03 +02:00
allcontributors[bot]
305ce29ebb docs: add oleynikd as a contributor for bug (#551)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

---------

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2024-07-30 11:14:12 +02:00
Ahmed Bouhuolia
9090d0a7b2 Merge pull request #548 from oleynikd/oleynikd-dev
Fixed Quick Payment Dialogs
2024-07-30 11:04:25 +02:00
Ahmed Bouhuolia
ffc55fa81b fix: quick payment received and payment made form initial values 2024-07-30 11:02:49 +02:00
Ahmed Bouhuolia
07c57ed539 Merge branch 'develop' into billing-subscription-page 2024-07-30 10:03:31 +02:00
Ahmed Bouhuolia
788150f80d Merge pull request #549 from oleynikd/s3-path-style
Added support of path-style S3 endpoints
2024-07-30 00:06:16 +02:00
Ahmed Bouhuolia
c4e77e4e3b fix: create quick payment received and payment made transactions 2024-07-29 23:15:42 +02:00
Denis
c09384e49b Added support of path-style S3 endpoints
This can be very useful when using S3-compatible object storages like MinIO
2024-07-29 23:48:29 +03:00
Denis
4490c2d4b4 Fixed Quick Payment Dialogs
PaymentReceives and BillsPayments Controllers expect 'amount' parameter, but webapp sends 'payment_amount'
2024-07-29 22:49:07 +03:00
Ahmed Bouhuolia
e11f1a95f6 Merge pull request #529 from bigcapitalhq/disconnect-bank-account
feat: Disconnect bank account
2024-07-29 20:18:36 +02:00
Ahmed Bouhuolia
b91273eee4 Merge branch 'develop' into disconnect-bank-account 2024-07-29 20:17:09 +02:00
Ahmed Bouhuolia
b5d570417b fix: add events interfaces of disconnect bank account 2024-07-29 20:10:15 +02:00
Ahmed Bouhuolia
acd3265e35 feat: add migration to is_syncing_owner column in accounts table 2024-07-29 20:01:04 +02:00
Ahmed Bouhuolia
894c899847 feat: improvement in Plaid accounts disconnecting 2024-07-29 19:49:20 +02:00
Ahmed Bouhuolia
f6d4ec504f feat: tweaks in disconnecting bank account 2024-07-29 16:55:50 +02:00
Ahmed Bouhuolia
1a01461f5d feat: delete Plaid item once bank account deleted 2024-07-29 16:20:59 +02:00
Ahmed Bouhuolia
f5e18fc1fe feat: document the Redux mutation methods 2024-07-29 14:03:37 +02:00
Ahmed Bouhuolia
f64cd32985 Merge branch 'develop' into bulk-categorize-bank-transactions 2024-07-29 13:03:35 +02:00
Ahmed Bouhuolia
89552d7ee2 Merge pull request #532 from bigcapitalhq/bulk-exclude-bank-transactions
feat: Bulk exclude bank transactions
2024-07-29 13:01:56 +02:00
Ahmed Bouhuolia
4345623ea9 feat: document functions 2024-07-29 13:00:50 +02:00
Ahmed Bouhuolia
f457759e39 Merge branch 'develop' into bulk-exclude-bank-transactions 2024-07-29 12:00:49 +02:00
Ahmed Bouhuolia
14d5e82b4a fix: style of database checkbox 2024-07-29 12:00:34 +02:00
Ahmed Bouhuolia
333b6f5a4b feat: change subscription plan 2024-07-28 20:52:53 +02:00
Ahmed Bouhuolia
1660df20af feat: wip billing page 2024-07-28 17:53:55 +02:00
Ahmed Bouhuolia
14a9c4ba28 fix: style tweaks in billing page 2024-07-27 21:56:55 +02:00
Ahmed Bouhuolia
383be111fa feat: style the billing page 2024-07-27 21:47:17 +02:00
Ahmed Bouhuolia
7720b1cc34 feat: getting subscription endpoint 2024-07-27 17:39:50 +02:00
Ahmed Bouhuolia
db634cbb79 feat: pause, resume main subscription 2024-07-27 16:55:56 +02:00
Ahmed Bouhuolia
998e6de211 feat: billing subscription page 2024-07-25 15:21:01 +02:00
Ahmed Bouhuolia
6fb02f9869 feat: bulk categorize and match bank transactions 2024-07-18 19:41:23 +02:00
Ahmed Bouhuolia
449390143d feat: bulk categorizing bank transactions 2024-07-18 17:00:23 +02:00
Ahmed Bouhuolia
51471ed000 feat: exclude bank transactions in bulk 2024-07-17 23:19:59 +02:00
Ahmed Bouhuolia
c2815afbe3 feat: disconnect and update bank account 2024-07-16 17:09:00 +02:00
Ahmed Bouhuolia
fa7e6b1fca feat: disconnect bank account 2024-07-15 23:18:39 +02:00
723 changed files with 14400 additions and 4012 deletions

View File

@@ -132,6 +132,33 @@
"contributions": [
"bug"
]
},
{
"login": "oleynikd",
"name": "Denis",
"avatar_url": "https://avatars.githubusercontent.com/u/3976868?v=4",
"profile": "https://github.com/oleynikd",
"contributions": [
"bug"
]
},
{
"login": "mittalsam98",
"name": "Sachin Mittal",
"avatar_url": "https://avatars.githubusercontent.com/u/42431274?v=4",
"profile": "https://myself.vercel.app/",
"contributions": [
"bug"
]
},
{
"login": "Champetaman",
"name": "Camilo Oviedo",
"avatar_url": "https://avatars.githubusercontent.com/u/64604272?v=4",
"profile": "https://www.camilooviedo.com/",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,

View File

@@ -89,3 +89,7 @@ S3_ACCESS_KEY_ID=
S3_SECRET_ACCESS_KEY=
S3_ENDPOINT=
S3_BUCKET=
# PostHog
POSTHOG_API_KEY=
POSTHOG_HOST=

View File

@@ -2,6 +2,27 @@
All notable changes to Bigcapital server-side will be in this file.
## [0.19.4] - 18-08-2024
* 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
* fix: Transaction type and description do not show in general ledger. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/596
* fix: Refresh accounts and account transactions. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/597
* fix: Typo payments made by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/598
* fix: Typo categories list by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/600
* fix: Autofill the quick created customer/vendor by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/601
* fix: Remove views tabs from receipts list by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/602
* fix: Typo payment receive messages by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/599
* fix: Enhance Dropzone visual of accept and reject modes by @Champetaman in https://github.com/bigcapitalhq/bigcapital/pull/603
* fix: Matching bank transactions should create associate payment transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/606
* fix: Change Dropzone title and subtitle by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/607
* fix: Inconsistance page size of paginated data tables by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/604
* fix: Database connection lost error by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/611
* fix: Language typos by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/613
* Fix: Correctly display Date, Published At, and Created At in ExpenseDrawerHeader by @Champetaman in https://github.com/bigcapitalhq/bigcapital/pull/612
* 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
## [v0.18.0] - 10-08-2024
* feat: Bank rules for automated categorization by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/511

View File

@@ -126,6 +126,11 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="http://vederis.id"><img src="https://avatars.githubusercontent.com/u/13505006?v=4?s=100" width="100px;" alt="Vederis Leunardus"/><br /><sub><b>Vederis Leunardus</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=cloudsbird" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.pivoten.com"><img src="https://avatars.githubusercontent.com/u/104120598?v=4?s=100" width="100px;" alt="Chris Cantrell"/><br /><sub><b>Chris Cantrell</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Accantrell72" title="Bug reports">🐛</a></td>
</tr>
<tr>
<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://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>
</tr>
</tbody>
</table>

View File

@@ -37,6 +37,7 @@
"agendash": "^3.1.0",
"app-root-path": "^3.0.0",
"async": "^3.2.0",
"async-mutex": "^0.5.0",
"axios": "^1.6.0",
"babel-loader": "^9.1.2",
"bcryptjs": "^2.4.3",
@@ -69,9 +70,8 @@
"is-my-json-valid": "^2.20.5",
"js-money": "^0.6.3",
"jsonwebtoken": "^8.5.1",
"knex": "^0.95.15",
"knex": "^3.1.0",
"knex-cleaner": "^1.3.0",
"knex-db-manager": "^0.6.1",
"libphonenumber-js": "^1.9.6",
"lodash": "^4.17.15",
"lru-cache": "^6.0.0",
@@ -99,6 +99,7 @@
"objection-unique": "^1.2.2",
"plaid": "^10.3.0",
"pluralize": "^8.0.0",
"posthog-node": "^4.2.0",
"pug": "^3.0.2",
"puppeteer": "^10.2.0",
"qim": "0.0.52",
@@ -108,6 +109,7 @@
"rtl-detect": "^1.0.4",
"socket.io": "^4.7.4",
"source-map-loader": "^4.0.1",
"swagger-ui-express": "^5.0.1",
"tmp-promise": "^3.0.3",
"ts-transformer-keys": "^0.4.2",
"tsyringe": "^4.3.0",

View File

@@ -103,24 +103,20 @@ export default class AccountsController extends BaseController {
check('name')
.exists()
.isLength({ min: 3, max: DATATYPES_LENGTH.STRING })
.trim()
.escape(),
.trim(),
check('code')
.optional({ nullable: true })
.isLength({ min: 3, max: 6 })
.trim()
.escape(),
.trim(),
check('currency_code').optional(),
check('account_type')
.exists()
.isLength({ min: 3, max: DATATYPES_LENGTH.STRING })
.trim()
.escape(),
.trim(),
check('description')
.optional({ nullable: true })
.isLength({ max: DATATYPES_LENGTH.TEXT })
.trim()
.escape(),
.trim(),
check('parent_account_id')
.optional({ nullable: true })
.isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 })
@@ -136,23 +132,19 @@ export default class AccountsController extends BaseController {
check('name')
.exists()
.isLength({ min: 3, max: DATATYPES_LENGTH.STRING })
.trim()
.escape(),
.trim(),
check('code')
.optional({ nullable: true })
.isLength({ min: 3, max: 6 })
.trim()
.escape(),
.trim(),
check('account_type')
.exists()
.isLength({ min: 3, max: DATATYPES_LENGTH.STRING })
.trim()
.escape(),
.trim(),
check('description')
.optional({ nullable: true })
.isLength({ max: DATATYPES_LENGTH.TEXT })
.trim()
.escape(),
.trim(),
check('parent_account_id')
.optional({ nullable: true })
.isInt({ min: 0, max: DATATYPES_LENGTH.INT_10 })

View File

@@ -5,7 +5,14 @@ import { body, param } from 'express-validator';
import BaseController from '@/api/controllers/BaseController';
import { AttachmentsApplication } from '@/services/Attachments/AttachmentsApplication';
import { AttachmentUploadPipeline } from '@/services/Attachments/S3UploadPipeline';
import {
ApiOperation,
ApiResponse,
ApiTags,
Route,
} from '@/decorators/swagger-decorators';
@ApiTags('Attachments')
@Service()
export class AttachmentsController extends BaseController {
@Inject()
@@ -121,6 +128,26 @@ export class AttachmentsController extends BaseController {
* @param {NextFunction} next
* @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(
req: Request,
res: Response,
@@ -250,10 +277,12 @@ export class AttachmentsController extends BaseController {
res: Response,
next: NextFunction
): Promise<Response | void> {
const { tenantId } = req;
const { id: documentKey } = req.params;
try {
const presignedUrl = await this.attachmentsApplication.getPresignedUrl(
tenantId,
documentKey
);
return res.status(200).send({ presignedUrl });

View File

@@ -90,27 +90,23 @@ export default class AuthenticationController extends BaseController {
.exists()
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('last_name')
.exists()
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('email')
.exists()
.isString()
.isEmail()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('password')
.exists()
.isString()
.isLength({ min: 6 })
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
];
}
@@ -150,7 +146,7 @@ export default class AuthenticationController extends BaseController {
* @returns {ValidationChain[]}
*/
private get sendResetPasswordSchema(): ValidationChain[] {
return [check('email').exists().isEmail().trim().escape()];
return [check('email').exists().isEmail().trim()];
}
/**
@@ -158,7 +154,11 @@ export default class AuthenticationController extends BaseController {
* @param {Request} req
* @param {Response} res
*/
private async login(req: Request, res: Response, next: Function): Response {
private async login(
req: Request,
res: Response,
next: Function
): Promise<Response | null> {
const userDTO: ILoginDTO = this.matchedBodyData(req);
try {

View File

@@ -1,14 +1,22 @@
import { Inject, Service } from 'typedi';
import { NextFunction, Request, Response, Router } from 'express';
import { param, query } from 'express-validator';
import BaseController from '@/api/controllers/BaseController';
import { CashflowApplication } from '@/services/Cashflow/CashflowApplication';
import { GetBankAccountSummary } from '@/services/Banking/BankAccounts/GetBankAccountSummary';
import { BankAccountsApplication } from '@/services/Banking/BankAccounts/BankAccountsApplication';
import { GetPendingBankAccountTransactions } from '@/services/Cashflow/GetPendingBankAccountTransaction';
@Service()
export class BankAccountsController extends BaseController {
@Inject()
private getBankAccountSummaryService: GetBankAccountSummary;
@Inject()
private bankAccountsApp: BankAccountsApplication;
@Inject()
private getPendingTransactionsService: GetPendingBankAccountTransactions;
/**
* Router constructor.
*/
@@ -16,6 +24,33 @@ export class BankAccountsController extends BaseController {
const router = Router();
router.get('/:bankAccountId/meta', this.getBankAccountSummary.bind(this));
router.get(
'/pending_transactions',
[
query('account_id').optional().isNumeric().toInt(),
query('page').optional().isNumeric().toInt(),
query('page_size').optional().isNumeric().toInt(),
],
this.validationResult,
this.getBankAccountsPendingTransactions.bind(this)
);
router.post(
'/:bankAccountId/disconnect',
this.disconnectBankAccount.bind(this)
);
router.post('/:bankAccountId/update', this.refreshBankAccount.bind(this));
router.post(
'/:bankAccountId/pause_feeds',
[param('bankAccountId').exists().isNumeric().toInt()],
this.validationResult,
this.pauseBankAccountFeeds.bind(this)
);
router.post(
'/:bankAccountId/resume_feeds',
[param('bankAccountId').exists().isNumeric().toInt()],
this.validationResult,
this.resumeBankAccountFeeds.bind(this)
);
return router;
}
@@ -46,4 +81,138 @@ export class BankAccountsController extends BaseController {
next(error);
}
}
/**
* Retrieves the bank account pending transactions.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async getBankAccountsPendingTransactions(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const query = this.matchedQueryData(req);
try {
const data =
await this.getPendingTransactionsService.getPendingTransactions(
tenantId,
query
);
return res.status(200).send(data);
} catch (error) {
next(error);
}
}
/**
* Disonnect the given bank account.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Promise<Response|null>}
*/
async disconnectBankAccount(
req: Request<{ bankAccountId: number }>,
res: Response,
next: NextFunction
) {
const { bankAccountId } = req.params;
const { tenantId } = req;
try {
await this.bankAccountsApp.disconnectBankAccount(tenantId, bankAccountId);
return res.status(200).send({
id: bankAccountId,
message: 'The bank account has been disconnected.',
});
} catch (error) {
next(error);
}
}
/**
* Refresh the given bank account.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Promise<Response|null>}
*/
async refreshBankAccount(
req: Request<{ bankAccountId: number }>,
res: Response,
next: NextFunction
) {
const { bankAccountId } = req.params;
const { tenantId } = req;
try {
await this.bankAccountsApp.refreshBankAccount(tenantId, bankAccountId);
return res.status(200).send({
id: bankAccountId,
message: 'The bank account has been disconnected.',
});
} catch (error) {
next(error);
}
}
/**
* Resumes the bank account feeds sync.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Promise<Response | void>}
*/
async resumeBankAccountFeeds(
req: Request<{ bankAccountId: number }>,
res: Response,
next: NextFunction
) {
const { bankAccountId } = req.params;
const { tenantId } = req;
try {
await this.bankAccountsApp.resumeBankAccount(tenantId, bankAccountId);
return res.status(200).send({
message: 'The bank account feeds syncing has been resumed.',
id: bankAccountId,
});
} catch (error) {
next(error);
}
}
/**
* Pauses the bank account feeds sync.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Promise<Response | void>}
*/
async pauseBankAccountFeeds(
req: Request<{ bankAccountId: number }>,
res: Response,
next: NextFunction
) {
const { bankAccountId } = req.params;
const { tenantId } = req;
try {
await this.bankAccountsApp.pauseBankAccount(tenantId, bankAccountId);
return res.status(200).send({
message: 'The bank account feeds syncing has been paused.',
id: bankAccountId,
});
} catch (error) {
next(error);
}
}
}

View File

@@ -1,12 +1,8 @@
import { Inject, Service } from 'typedi';
import { body, param } from 'express-validator';
import { NextFunction, Request, Response, Router } from 'express';
import BaseController from '@/api/controllers/BaseController';
import { MatchBankTransactionsApplication } from '@/services/Banking/Matching/MatchBankTransactionsApplication';
import { body, param } from 'express-validator';
import {
GetMatchedTransactionsFilter,
IMatchTransactionsDTO,
} from '@/services/Banking/Matching/types';
@Service()
export class BankTransactionsMatchingController extends BaseController {
@@ -20,9 +16,17 @@ export class BankTransactionsMatchingController extends BaseController {
const router = Router();
router.post(
'/:transactionId',
'/unmatch/:transactionId',
[param('transactionId').exists()],
this.validationResult,
this.unmatchMatchedBankTransaction.bind(this)
);
router.post(
'/match',
[
param('transactionId').exists(),
body('uncategorizedTransactions').exists().isArray({ min: 1 }),
body('uncategorizedTransactions.*').isNumeric().toInt(),
body('matchedTransactions').isArray({ min: 1 }),
body('matchedTransactions.*.reference_type').exists(),
body('matchedTransactions.*.reference_id').isNumeric().toInt(),
@@ -30,12 +34,6 @@ export class BankTransactionsMatchingController extends BaseController {
this.validationResult,
this.matchBankTransaction.bind(this)
);
router.post(
'/unmatch/:transactionId',
[param('transactionId').exists()],
this.validationResult,
this.unmatchMatchedBankTransaction.bind(this)
);
return router;
}
@@ -50,21 +48,21 @@ export class BankTransactionsMatchingController extends BaseController {
req: Request<{ transactionId: number }>,
res: Response,
next: NextFunction
) {
): Promise<Response | null> {
const { tenantId } = req;
const { transactionId } = req.params;
const matchTransactionDTO = this.matchedBodyData(
req
) as IMatchTransactionsDTO;
const bodyData = this.matchedBodyData(req);
const uncategorizedTransactions = bodyData?.uncategorizedTransactions;
const matchedTransactions = bodyData?.matchedTransactions;
try {
await this.bankTransactionsMatchingApp.matchTransaction(
tenantId,
transactionId,
matchTransactionDTO
uncategorizedTransactions,
matchedTransactions
);
return res.status(200).send({
id: transactionId,
ids: uncategorizedTransactions,
message: 'The bank transaction has been matched.',
});
} catch (error) {

View File

@@ -6,6 +6,7 @@ import { BankingRulesController } from './BankingRulesController';
import { BankTransactionsMatchingController } from './BankTransactionsMatchingController';
import { RecognizedTransactionsController } from './RecognizedTransactionsController';
import { BankAccountsController } from './BankAccountsController';
import { BankingUncategorizedController } from './BankingUncategorizedController';
@Service()
export class BankingController extends BaseController {
@@ -29,6 +30,10 @@ export class BankingController extends BaseController {
'/bank_accounts',
Container.get(BankAccountsController).router()
);
router.use(
'/categorize',
Container.get(BankingUncategorizedController).router()
);
return router;
}
}

View File

@@ -33,17 +33,25 @@ export class BankingRulesController extends BaseController {
body('conditions.*.field').exists().isIn(['description', 'amount']),
body('conditions.*.comparator')
.exists()
.isIn(['equals', 'contains', 'not_contain'])
.default('contain'),
body('conditions.*.value').exists(),
.isIn([
'equals',
'equal',
'contains',
'not_contain',
'bigger',
'bigger_or_equal',
'smaller',
'smaller_or_equal',
])
.default('contain')
.trim(),
body('conditions.*.value').exists().trim(),
// Assign
body('assign_category').isString(),
body('assign_account_id').isInt({ min: 0 }),
body('assign_payee').isString().optional({ nullable: true }),
body('assign_memo').isString().optional({ nullable: true }),
body('recognition').isBoolean().toBoolean().optional({ nullable: true }),
];
}

View File

@@ -0,0 +1,57 @@
import { Inject, Service } from 'typedi';
import { NextFunction, Request, Response, Router } from 'express';
import { query } from 'express-validator';
import BaseController from '../BaseController';
import { GetAutofillCategorizeTransaction } from '@/services/Banking/RegonizeTranasctions/GetAutofillCategorizeTransaction';
@Service()
export class BankingUncategorizedController extends BaseController {
@Inject()
private getAutofillCategorizeTransactionService: GetAutofillCategorizeTransaction;
/**
* Router constructor.
*/
router() {
const router = Router();
router.get(
'/autofill',
[
query('uncategorizedTransactionIds').isArray({ min: 1 }),
query('uncategorizedTransactionIds.*').isNumeric().toInt(),
],
this.validationResult,
this.getAutofillCategorizeTransaction.bind(this)
);
return router;
}
/**
* Retrieves the autofill values of the categorize form of the given
* uncategorized transactions.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Promise<Response | null>}
*/
public async getAutofillCategorizeTransaction(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const uncategorizedTransactionIds = req.query.uncategorizedTransactionIds;
try {
const data =
await this.getAutofillCategorizeTransactionService.getAutofillCategorizeTransaction(
tenantId,
uncategorizedTransactionIds
);
return res.status(200).send({ data });
} catch (error) {
next(error);
}
}
}

View File

@@ -1,8 +1,9 @@
import { Inject, Service } from 'typedi';
import { param } from 'express-validator';
import { NextFunction, Request, Response, Router, query } from 'express';
import { body, param, query } from 'express-validator';
import { NextFunction, Request, Response, Router } from 'express';
import BaseController from '../BaseController';
import { ExcludeBankTransactionsApplication } from '@/services/Banking/Exclude/ExcludeBankTransactionsApplication';
import { map, parseInt, trim } from 'lodash';
@Service()
export class ExcludeBankTransactionsController extends BaseController {
@@ -15,9 +16,21 @@ export class ExcludeBankTransactionsController extends BaseController {
public router() {
const router = Router();
router.put(
'/transactions/exclude',
[body('ids').exists()],
this.validationResult,
this.excludeBulkBankTransactions.bind(this)
);
router.put(
'/transactions/unexclude',
[body('ids').exists()],
this.validationResult,
this.unexcludeBulkBankTransactins.bind(this)
);
router.put(
'/transactions/:transactionId/exclude',
[param('transactionId').exists()],
[param('transactionId').exists().toInt()],
this.validationResult,
this.excludeBankTransaction.bind(this)
);
@@ -29,7 +42,15 @@ export class ExcludeBankTransactionsController extends BaseController {
);
router.get(
'/excluded',
[],
[
query('account_id').optional().isNumeric().toInt(),
query('page').optional().isNumeric().toInt(),
query('page_size').optional().isNumeric().toInt(),
query('min_date').optional({ nullable: true }).isISO8601().toDate(),
query('max_date').optional({ nullable: true }).isISO8601().toDate(),
query('min_amount').optional({ nullable: true }).isFloat().toFloat(),
query('max_amount').optional({ nullable: true }).isFloat().toFloat(),
],
this.validationResult,
this.getExcludedBankTransactions.bind(this)
);
@@ -94,6 +115,63 @@ export class ExcludeBankTransactionsController extends BaseController {
}
}
/**
* Exclude bank transactions in bulk.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
private async excludeBulkBankTransactions(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const { ids } = this.matchedBodyData(req);
try {
await this.excludeBankTransactionApp.excludeBankTransactions(
tenantId,
ids
);
return res.status(200).send({
message: 'The given bank transactions have been excluded',
ids,
});
} catch (error) {
next(error);
}
}
/**
* Unexclude the given bank transactions in bulk.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Promise<Response | null>}
*/
private async unexcludeBulkBankTransactins(
req: Request,
res: Response,
next: NextFunction
): Promise<Response | null> {
const { tenantId } = req;
const { ids } = this.matchedBodyData(req);
try {
await this.excludeBankTransactionApp.unexcludeBankTransactions(
tenantId,
ids
);
return res.status(200).send({
message: 'The given bank transactions have been excluded',
ids,
});
} catch (error) {
next(error);
}
}
/**
* Retrieves the excluded uncategorized bank transactions.
* @param {Request} req
@@ -107,9 +185,8 @@ export class ExcludeBankTransactionsController extends BaseController {
next: NextFunction
): Promise<Response | void> {
const { tenantId } = req;
const filter = this.matchedBodyData(req);
const filter = this.matchedQueryData(req);
console.log('123');
try {
const data =
await this.excludeBankTransactionApp.getExcludedBankTransactions(

View File

@@ -1,5 +1,6 @@
import { Inject, Service } from 'typedi';
import { NextFunction, Request, Response, Router } from 'express';
import { query } from 'express-validator';
import BaseController from '@/api/controllers/BaseController';
import { CashflowApplication } from '@/services/Cashflow/CashflowApplication';
@@ -14,7 +15,20 @@ export class RecognizedTransactionsController extends BaseController {
router() {
const router = Router();
router.get('/', this.getRecognizedTransactions.bind(this));
router.get(
'/',
[
query('page').optional().isNumeric().toInt(),
query('page_size').optional().isNumeric().toInt(),
query('account_id').optional().isNumeric().toInt(),
query('min_date').optional({ nullable: true }).isISO8601().toDate(),
query('max_date').optional({ nullable: true }).isISO8601().toDate(),
query('min_amount').optional({ nullable: true }).isFloat().toFloat(),
query('max_amount').optional({ nullable: true }).isFloat().isFloat(),
],
this.validationResult,
this.getRecognizedTransactions.bind(this)
);
router.get(
'/transactions/:uncategorizedTransactionId',
this.getRecognizedTransaction.bind(this)

View File

@@ -1,6 +1,6 @@
import { Service, Inject } from 'typedi';
import { Router, Request, Response, NextFunction } from 'express';
import { param } from 'express-validator';
import { param, query } from 'express-validator';
import BaseController from '../BaseController';
import { ServiceError } from '@/exceptions';
import CheckPolicies from '@/api/middleware/CheckPolicies';
@@ -24,7 +24,12 @@ export default class GetCashflowAccounts extends BaseController {
const router = Router();
router.get(
'/transactions/:transactionId/matches',
'/transactions/matches',
[
query('uncategorizeTransactionsIds').exists().isArray({ min: 1 }),
query('uncategorizeTransactionsIds.*').exists().isNumeric().toInt(),
],
this.validationResult,
this.getMatchedTransactions.bind(this)
);
router.get(
@@ -44,7 +49,7 @@ export default class GetCashflowAccounts extends BaseController {
* @param {NextFunction} next
*/
private getCashflowTransaction = async (
req: Request,
req: Request<{ transactionId: number }>,
res: Response,
next: NextFunction
) => {
@@ -71,19 +76,24 @@ export default class GetCashflowAccounts extends BaseController {
* @param {NextFunction} next
*/
private async getMatchedTransactions(
req: Request<{ transactionId: number }>,
req: Request<
{ transactionId: number },
null,
null,
{ uncategorizeTransactionsIds: Array<number> }
>,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const { transactionId } = req.params;
const uncategorizeTransactionsIds = req.query.uncategorizeTransactionsIds;
const filter = this.matchedQueryData(req) as GetMatchedTransactionsFilter;
try {
const data =
await this.bankTransactionsMatchingApp.getMatchedTransactions(
tenantId,
transactionId,
uncategorizeTransactionsIds,
filter
);
return res.status(200).send(data);

View File

@@ -1,10 +1,15 @@
import { Service, Inject } from 'typedi';
import { ValidationChain, check, param, query } from 'express-validator';
import { ValidationChain, body, check, param, query } from 'express-validator';
import { Router, Request, Response, NextFunction } from 'express';
import { omit } from 'lodash';
import BaseController from '../BaseController';
import { ServiceError } from '@/exceptions';
import CheckPolicies from '@/api/middleware/CheckPolicies';
import { AbilitySubject, CashflowAction } from '@/interfaces';
import {
AbilitySubject,
CashflowAction,
ICategorizeCashflowTransactioDTO,
} from '@/interfaces';
import { CashflowApplication } from '@/services/Cashflow/CashflowApplication';
@Service()
@@ -38,13 +43,23 @@ export default class NewCashflowTransactionController extends BaseController {
this.asyncMiddleware(this.newCashflowTransaction),
this.catchServiceErrors
);
router.post(
'/transactions/uncategorize/bulk',
[
body('ids').isArray({ min: 1 }),
body('ids.*').exists().isNumeric().toInt(),
],
this.validationResult,
this.uncategorizeBulkTransactions.bind(this),
this.catchServiceErrors
);
router.post(
'/transactions/:id/uncategorize',
this.revertCategorizedCashflowTransaction,
this.catchServiceErrors
);
router.post(
'/transactions/:id/categorize',
'/transactions/categorize',
this.categorizeCashflowTransactionValidationSchema,
this.validationResult,
this.categorizeCashflowTransaction,
@@ -69,6 +84,10 @@ export default class NewCashflowTransactionController extends BaseController {
param('id').exists().isNumeric().toInt(),
query('page').optional().isNumeric().toInt(),
query('page_size').optional().isNumeric().toInt(),
query('min_date').optional({ nullable: true }).isISO8601().toDate(),
query('max_date').optional({ nullable: true }).isISO8601().toDate(),
query('min_amount').optional({ nullable: true }).isFloat().toFloat(),
query('max_amount').optional({ nullable: true }).isFloat().toFloat(),
];
}
@@ -89,6 +108,7 @@ export default class NewCashflowTransactionController extends BaseController {
*/
public get categorizeCashflowTransactionValidationSchema() {
return [
check('uncategorized_transaction_ids').exists().isArray({ min: 1 }),
check('date').exists().isISO8601().toDate(),
check('credit_account_id').exists().isInt().toInt(),
check('transaction_number').optional(),
@@ -106,12 +126,11 @@ export default class NewCashflowTransactionController extends BaseController {
public get newTransactionValidationSchema() {
return [
check('date').exists().isISO8601().toDate(),
check('reference_no').optional({ nullable: true }).trim().escape(),
check('reference_no').optional({ nullable: true }).trim(),
check('description')
.optional({ nullable: true })
.isLength({ min: 3 })
.trim()
.escape(),
.trim(),
check('transaction_type').exists(),
check('amount').exists().isFloat().toFloat(),
@@ -161,7 +180,7 @@ export default class NewCashflowTransactionController extends BaseController {
* @param {NextFunction} next
*/
private revertCategorizedCashflowTransaction = async (
req: Request,
req: Request<{ id: number }>,
res: Response,
next: NextFunction
) => {
@@ -179,6 +198,34 @@ export default class NewCashflowTransactionController extends BaseController {
}
};
/**
* Uncategorize the given transactions in bulk.
* @param {Request<{}>} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Promise<Response | null>}
*/
private uncategorizeBulkTransactions = async (
req: Request<{}>,
res: Response,
next: NextFunction
) => {
const { tenantId } = req;
const { ids: uncategorizedTransactionIds } = this.matchedBodyData(req);
try {
await this.cashflowApplication.uncategorizeTransactions(
tenantId,
uncategorizedTransactionIds
);
return res.status(200).send({
message: 'The given transactions have been uncategorized successfully.',
});
} catch (error) {
next(error);
}
};
/**
* Categorize the cashflow transaction.
* @param {Request} req
@@ -191,14 +238,19 @@ export default class NewCashflowTransactionController extends BaseController {
next: NextFunction
) => {
const { tenantId } = req;
const { id: cashflowTransactionId } = req.params;
const cashflowTransaction = this.matchedBodyData(req);
const matchedObject = this.matchedBodyData(req);
const categorizeDTO = omit(matchedObject, [
'uncategorizedTransactionIds',
]) as ICategorizeCashflowTransactioDTO;
const uncategorizedTransactionIds =
matchedObject.uncategorizedTransactionIds;
try {
await this.cashflowApplication.categorizeTransaction(
tenantId,
cashflowTransactionId,
cashflowTransaction
uncategorizedTransactionIds,
categorizeDTO
);
return res.status(200).send({
message: 'The cashflow transaction has been created successfully.',
@@ -269,7 +321,7 @@ export default class NewCashflowTransactionController extends BaseController {
* @param {NextFunction} next
*/
public getUncategorizedCashflowTransactions = async (
req: Request,
req: Request<{ id: number }>,
res: Response,
next: NextFunction
) => {

View File

@@ -56,7 +56,7 @@ export default class ContactsController extends BaseController {
*/
get autocompleteQuerySchema() {
return [
query('column_sort_by').optional().trim().escape(),
query('column_sort_by').optional().trim(),
query('sort_order').optional().isIn(['desc', 'asc']),
query('stringified_filter_roles').optional().isJSON(),
@@ -122,32 +122,27 @@ export default class ContactsController extends BaseController {
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('first_name')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('last_name')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('company_name')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('display_name')
.exists()
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('email')
@@ -165,120 +160,101 @@ export default class ContactsController extends BaseController {
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('personal_phone')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('billing_address_1')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('billing_address_2')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('billing_address_city')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('billing_address_country')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('billing_address_email')
.optional({ nullable: true })
.isString()
.isEmail()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('billing_address_postcode')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('billing_address_phone')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('billing_address_state')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('shipping_address_1')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('shipping_address_2')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('shipping_address_city')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('shipping_address_country')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('shipping_address_email')
.optional({ nullable: true })
.isString()
.isEmail()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('shipping_address_postcode')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('shipping_address_phone')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('shipping_address_state')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('note')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.TEXT }),
check('active').optional().isBoolean().toBoolean(),
];

View File

@@ -106,11 +106,7 @@ export default class CustomersController extends ContactsController {
*/
get customerDTOSchema() {
return [
check('customer_type')
.exists()
.isIn(['business', 'individual'])
.trim()
.escape(),
check('customer_type').exists().isIn(['business', 'individual']).trim(),
];
}
@@ -123,7 +119,6 @@ export default class CustomersController extends ContactsController {
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: 3 }),
];
}
@@ -133,7 +128,7 @@ export default class CustomersController extends ContactsController {
*/
get validateListQuerySchema() {
return [
query('column_sort_by').optional().trim().escape(),
query('column_sort_by').optional().trim(),
query('sort_order').optional().isIn(['desc', 'asc']),
query('page').optional().isNumeric().toInt(),

View File

@@ -106,7 +106,6 @@ export default class VendorsController extends ContactsController {
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ min: 3, max: 3 }),
];
}

View File

@@ -67,7 +67,7 @@ export default class CurrenciesController extends BaseController {
}
get currencyParamSchema(): ValidationChain[] {
return [param('currency_code').exists().trim().escape()];
return [param('currency_code').exists().trim()];
}
get listSchema(): ValidationChain[] {
@@ -187,11 +187,13 @@ export default class CurrenciesController extends BaseController {
}
if (error.errorType === 'currency_code_exists') {
return res.boom.badRequest(null, {
errors: [{
type: 'CURRENCY_CODE_EXISTS',
message: 'The given currency code is already exists.',
code: 200,
}],
errors: [
{
type: 'CURRENCY_CODE_EXISTS',
message: 'The given currency code is already exists.',
code: 200,
},
],
});
}
if (error.errorType === 'CANNOT_DELETE_BASE_CURRENCY') {

View File

@@ -89,7 +89,6 @@ export class ExpensesController extends BaseController {
check('reference_no')
.optional({ nullable: true })
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('payment_date').exists().isISO8601().toDate(),
check('payment_account_id')
@@ -123,7 +122,6 @@ export class ExpensesController extends BaseController {
check('categories.*.description')
.optional()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('categories.*.landed_cost').optional().isBoolean().toBoolean(),
check('categories.*.project_id')
@@ -144,7 +142,6 @@ export class ExpensesController extends BaseController {
check('reference_no')
.optional({ nullable: true })
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('payment_date').exists().isISO8601().toDate(),
check('payment_account_id')
@@ -179,7 +176,6 @@ export class ExpensesController extends BaseController {
check('categories.*.description')
.optional()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('categories.*.landed_cost').optional().isBoolean().toBoolean(),
check('categories.*.project_id')

View File

@@ -1,9 +1,7 @@
import { query } from 'express-validator';
import BaseController from "../BaseController";
import BaseController from '../BaseController';
export default class BaseFinancialReportController extends BaseController {
get sheetNumberFormatValidationSchema() {
return [
query('number_format.precision')
@@ -19,8 +17,7 @@ export default class BaseFinancialReportController extends BaseController {
query('number_format.negative_format')
.optional()
.isIn(['parentheses', 'mines'])
.trim()
.escape(),
.trim(),
];
}
}
}

View File

@@ -51,8 +51,7 @@ export default class InventoryDetailsController extends BaseController {
query('number_format.negative_format')
.optional()
.isIn(['parentheses', 'mines'])
.trim()
.escape(),
.trim(),
query('from_date').optional(),
query('to_date').optional(),

View File

@@ -36,7 +36,7 @@ export default class JournalSheetController extends BaseFinancialReportControlle
return [
query('from_date').optional().isISO8601(),
query('to_date').optional().isISO8601(),
query('transaction_type').optional().trim().escape(),
query('transaction_type').optional().trim(),
query('transaction_id').optional().isInt().toInt(),
oneOf(
[

View File

@@ -40,8 +40,7 @@ export default class TransactionsByReferenceController extends BaseController {
query('number_format.negative_format')
.optional()
.isIn(['parentheses', 'mines'])
.trim()
.escape(),
.trim(),
];
}

View File

@@ -86,7 +86,7 @@ export default class InventoryAdjustmentsController extends BaseController {
*/
get validateListQuerySchema() {
return [
query('column_sort_by').optional().trim().escape(),
query('column_sort_by').optional().trim(),
query('sort_order').optional().isIn(['desc', 'asc']),
query('page').optional().isNumeric().toInt(),

View File

@@ -25,7 +25,7 @@ export default class InviteUsersController extends BaseController {
router.post(
'/send',
[
body('email').exists().trim().escape(),
body('email').exists().trim(),
body('role_id').exists().isNumeric().toInt(),
],
this.validationResult,
@@ -57,7 +57,7 @@ export default class InviteUsersController extends BaseController {
);
router.get(
'/invited/:token',
[param('token').exists().trim().escape()],
[param('token').exists().trim()],
this.validationResult,
asyncMiddleware(this.invited.bind(this)),
this.handleServicesError
@@ -72,10 +72,10 @@ export default class InviteUsersController extends BaseController {
*/
private get inviteUserDTO() {
return [
check('first_name').exists().trim().escape(),
check('last_name').exists().trim().escape(),
check('password').exists().trim().escape().isLength({ min: 5 }),
param('token').exists().trim().escape(),
check('first_name').exists().trim(),
check('last_name').exists().trim(),
check('password').exists().trim().isLength({ min: 5 }),
param('token').exists().trim(),
];
}

View File

@@ -73,13 +73,11 @@ export default class ItemsCategoriesController extends BaseController {
check('name')
.exists()
.trim()
.escape()
.isLength({ min: 0, max: DATATYPES_LENGTH.STRING }),
check('description')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.TEXT }),
check('sell_account_id')
.optional({ nullable: true })
@@ -101,9 +99,8 @@ export default class ItemsCategoriesController extends BaseController {
*/
get categoriesListValidationSchema() {
return [
query('column_sort_by').optional().trim().escape(),
query('sort_order').optional().trim().escape().isIn(['desc', 'asc']),
query('column_sort_by').optional().trim(),
query('sort_order').optional().trim().isIn(['desc', 'asc']),
query('stringified_filter_roles').optional().isJSON(),
];
}
@@ -207,14 +204,12 @@ export default class ItemsCategoriesController extends BaseController {
};
try {
const {
itemCategories,
filterMeta,
} = await this.itemCategoriesService.getItemCategoriesList(
tenantId,
itemCategoriesFilter,
user
);
const { itemCategories, filterMeta } =
await this.itemCategoriesService.getItemCategoriesList(
tenantId,
itemCategoriesFilter,
user
);
return res.status(200).send({
item_categories: itemCategories,
filter_meta: this.transfromToResponse(filterMeta),

View File

@@ -8,6 +8,11 @@ import { IItemDTO, ItemAction, AbilitySubject } from '@/interfaces';
import { DATATYPES_LENGTH } from '@/data/DataTypes';
import CheckAbilities from '@/api/middleware/CheckPolicies';
import { ItemsApplication } from '@/services/Items/ItemsApplication';
import {
ApiOperation,
ApiResponse,
Route,
} from '@/decorators/swagger-decorators';
@Service()
export default class ItemsController extends BaseController {
@@ -96,13 +101,11 @@ export default class ItemsController extends BaseController {
.exists()
.isString()
.trim()
.escape()
.isIn(['service', 'non-inventory', 'inventory']),
check('code')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
// Purchase attributes.
check('purchasable').optional().isBoolean().toBoolean(),
@@ -141,13 +144,11 @@ export default class ItemsController extends BaseController {
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.TEXT }),
check('purchase_description')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.TEXT }),
check('sell_tax_rate_id').optional({ nullable: true }).isInt().toInt(),
check('purchase_tax_rate_id')
@@ -162,7 +163,6 @@ export default class ItemsController extends BaseController {
.optional()
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.TEXT }),
check('active').optional().isBoolean().toBoolean(),
@@ -184,7 +184,7 @@ export default class ItemsController extends BaseController {
*/
private get validateListQuerySchema() {
return [
query('column_sort_by').optional().trim().escape(),
query('column_sort_by').optional().trim(),
query('sort_order').optional().isIn(['desc', 'asc']),
query('page').optional().isNumeric().toInt(),
@@ -203,6 +203,22 @@ export default class ItemsController extends BaseController {
* @param {Request} req
* @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) {
const { tenantId } = req;
const itemDTO: IItemDTO = this.matchedBodyData(req);

View File

@@ -94,25 +94,21 @@ export default class ManualJournalsController extends BaseController {
.optional()
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('journal_type')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('reference')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.STRING }),
check('description')
.optional({ nullable: true })
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.TEXT }),
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
check('publish').optional().isBoolean().toBoolean(),
@@ -163,7 +159,7 @@ export default class ManualJournalsController extends BaseController {
query('page_size').optional().isNumeric().toInt(),
query('custom_view_id').optional().isNumeric().toInt(),
query('column_sort_by').optional().trim().escape(),
query('column_sort_by').optional().trim(),
query('sort_order').optional().isIn(['desc', 'asc']),
query('stringified_filter_roles').optional().isJSON(),

View File

@@ -61,15 +61,14 @@ export default class MediaController extends BaseController {
get uploadValidationSchema() {
return [
// check('attachment'),
check('model_name').optional().trim().escape(),
check('model_id').optional().isNumeric().toInt(),
check('model_name').optional().trim(),
check('model_id').optional().isNumeric(),
];
}
get linkValidationSchema() {
return [
check('model_name').exists().trim().escape(),
check('model_name').exists().trim(),
check('model_id').exists().isNumeric().toInt(),
]
}

View File

@@ -0,0 +1,87 @@
import { Router, Request, Response, NextFunction } from 'express';
import { Service, Inject } from 'typedi';
import { body } from 'express-validator';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import BaseController from '@/api/controllers/BaseController';
import { OneClickDemoApplication } from '@/services/OneClickDemo/OneClickDemoApplication';
import config from '@/config';
@Service()
export class OneClickDemoController extends BaseController {
@Inject()
private oneClickDemoApp: OneClickDemoApplication;
/**
* Router constructor method.
*/
router() {
const router = Router();
// Protects the endpoints if the feature is not enabled.
const protectMiddleware = (
req: Request,
res: Response,
next: NextFunction
) => {
// Add your protection logic here
if (config.oneClickDemoAccounts) {
next();
} else {
res.status(403).send({ message: 'Forbidden' });
}
};
router.post(
'/one_click',
protectMiddleware,
asyncMiddleware(this.oneClickDemo.bind(this))
);
router.post(
'/one_click_signin',
[body('demo_id').exists()],
this.validationResult,
protectMiddleware,
asyncMiddleware(this.oneClickSignIn.bind(this))
);
return router;
}
/**
* One-click demo application.
* @param {Request} req -
* @param {Response} res -
* @param {NextFunction} next -
*/
private async oneClickDemo(req: Request, res: Response, next: NextFunction) {
try {
const data = await this.oneClickDemoApp.createOneClick();
return res.status(200).send({
data,
message: 'The one-click demo has been created successfully.',
});
} catch (error) {
next(error);
}
}
/**
* Sign-in to one-click demo account.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
private async oneClickSignIn(
req: Request,
res: Response,
next: NextFunction
) {
const { demoId } = this.matchedBodyData(req);
try {
const data = await this.oneClickDemoApp.autoSignIn(demoId);
return res.status(200).send(data);
} catch (error) {
next(error);
}
}
}

View File

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

View File

@@ -100,8 +100,8 @@ export default class BillsController extends BaseController {
*/
private get billValidationSchema() {
return [
check('bill_number').exists().trim().escape(),
check('reference_no').optional().trim().escape(),
check('bill_number').exists().trim(),
check('reference_no').optional().trim(),
check('bill_date').exists().isISO8601(),
check('due_date').optional().isISO8601(),
@@ -112,7 +112,7 @@ export default class BillsController extends BaseController {
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
check('project_id').optional({ nullable: true }).isNumeric().toInt(),
check('note').optional().trim().escape(),
check('note').optional().trim(),
check('open').default(false).isBoolean().toBoolean(),
check('is_inclusive_tax').default(false).isBoolean().toBoolean(),
@@ -126,10 +126,7 @@ export default class BillsController extends BaseController {
.optional({ nullable: true })
.isNumeric()
.toFloat(),
check('entries.*.description')
.optional({ nullable: true })
.trim()
.escape(),
check('entries.*.description').optional({ nullable: true }).trim(),
check('entries.*.landed_cost')
.optional({ nullable: true })
.isBoolean()
@@ -141,7 +138,6 @@ export default class BillsController extends BaseController {
check('entries.*.tax_code')
.optional({ nullable: true })
.trim()
.escape()
.isString(),
check('entries.*.tax_rate_id')
.optional({ nullable: true })
@@ -158,8 +154,8 @@ export default class BillsController extends BaseController {
*/
private get billEditValidationSchema() {
return [
check('bill_number').optional().trim().escape(),
check('reference_no').optional().trim().escape(),
check('bill_number').optional().trim(),
check('reference_no').optional().trim(),
check('bill_date').exists().isISO8601(),
check('due_date').optional().isISO8601(),
@@ -170,7 +166,7 @@ export default class BillsController extends BaseController {
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
check('project_id').optional({ nullable: true }).isNumeric().toInt(),
check('note').optional().trim().escape(),
check('note').optional().trim(),
check('open').default(false).isBoolean().toBoolean(),
check('entries').isArray({ min: 1 }),
@@ -184,10 +180,7 @@ export default class BillsController extends BaseController {
.optional({ nullable: true })
.isNumeric()
.toFloat(),
check('entries.*.description')
.optional({ nullable: true })
.trim()
.escape(),
check('entries.*.description').optional({ nullable: true }).trim(),
check('entries.*.landed_cost')
.optional({ nullable: true })
.isBoolean()
@@ -222,8 +215,8 @@ export default class BillsController extends BaseController {
private get dueBillsListingValidationSchema() {
return [
query('vendor_id').optional().trim().escape(),
query('payment_made_id').optional().trim().escape(),
query('vendor_id').optional().trim(),
query('payment_made_id').optional().trim(),
];
}

View File

@@ -113,10 +113,10 @@ export default class BillsPayments extends BaseController {
check('amount').exists().isNumeric().toFloat(),
check('payment_account_id').exists().isNumeric().toInt(),
check('payment_number').optional({ nullable: true }).trim().escape(),
check('payment_number').optional({ nullable: true }).trim(),
check('payment_date').exists(),
check('statement').optional().trim().escape(),
check('reference').optional().trim().escape(),
check('statement').optional().trim(),
check('reference').optional().trim(),
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
check('entries').exists().isArray(),

View File

@@ -156,13 +156,10 @@ export default class VendorCreditController extends BaseController {
check('vendor_id').exists().isNumeric().toInt(),
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
check('vendor_credit_number')
.optional({ nullable: true })
.trim()
.escape(),
check('reference_no').optional().trim().escape(),
check('vendor_credit_number').optional({ nullable: true }).trim(),
check('reference_no').optional().trim(),
check('vendor_credit_date').exists().isISO8601().toDate(),
check('note').optional().trim().escape(),
check('note').optional().trim(),
check('open').default(false).isBoolean().toBoolean(),
check('warehouse_id').optional({ nullable: true }).isNumeric().toInt(),
@@ -178,10 +175,7 @@ export default class VendorCreditController extends BaseController {
.optional({ nullable: true })
.isNumeric()
.toFloat(),
check('entries.*.description')
.optional({ nullable: true })
.trim()
.escape(),
check('entries.*.description').optional({ nullable: true }).trim(),
check('entries.*.warehouse_id')
.optional({ nullable: true })
.isNumeric()
@@ -202,13 +196,10 @@ export default class VendorCreditController extends BaseController {
check('vendor_id').exists().isNumeric().toInt(),
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
check('vendor_credit_number')
.optional({ nullable: true })
.trim()
.escape(),
check('reference_no').optional().trim().escape(),
check('vendor_credit_number').optional({ nullable: true }).trim(),
check('reference_no').optional().trim(),
check('vendor_credit_date').exists().isISO8601().toDate(),
check('note').optional().trim().escape(),
check('note').optional().trim(),
check('warehouse_id').optional({ nullable: true }).isNumeric().toInt(),
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
@@ -223,10 +214,7 @@ export default class VendorCreditController extends BaseController {
.optional({ nullable: true })
.isNumeric()
.toFloat(),
check('entries.*.description')
.optional({ nullable: true })
.trim()
.escape(),
check('entries.*.description').optional({ nullable: true }).trim(),
check('entries.*.warehouse_id')
.optional({ nullable: true })
.isNumeric()

View File

@@ -18,9 +18,7 @@ export default class ResourceController extends BaseController {
router.get(
'/:resource_model/meta',
[
param('resource_model').exists().trim().escape()
],
[param('resource_model').exists().trim()],
this.asyncMiddleware(this.resourceMeta.bind(this)),
this.handleServiceErrors
);
@@ -48,9 +46,7 @@ export default class ResourceController extends BaseController {
resourceModel
);
return res.status(200).send({
resource_meta: this.transfromToResponse(
resourceMeta,
),
resource_meta: this.transfromToResponse(resourceMeta),
});
} catch (error) {
next(error);

View File

@@ -210,9 +210,9 @@ export default class PaymentReceivesController extends BaseController {
check('credit_note_date').exists().isISO8601().toDate(),
check('reference_no').optional(),
check('credit_note_number').optional({ nullable: true }).trim().escape(),
check('note').optional().trim().escape(),
check('terms_conditions').optional().trim().escape(),
check('credit_note_number').optional({ nullable: true }).trim(),
check('note').optional().trim(),
check('terms_conditions').optional().trim(),
check('open').default(false).isBoolean().toBoolean(),
check('warehouse_id').optional({ nullable: true }).isNumeric().toInt(),
@@ -228,10 +228,7 @@ export default class PaymentReceivesController extends BaseController {
.optional({ nullable: true })
.isNumeric()
.toFloat(),
check('entries.*.description')
.optional({ nullable: true })
.trim()
.escape(),
check('entries.*.description').optional({ nullable: true }).trim(),
check('entries.*.warehouse_id')
.optional({ nullable: true })
.isNumeric()

View File

@@ -9,9 +9,9 @@ import {
} from '@/interfaces';
import BaseController from '@/api/controllers/BaseController';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import PaymentReceivesPages from '@/services/Sales/PaymentReceives/PaymentReceivesPages';
import PaymentsReceivedPages from '@/services/Sales/PaymentReceived/PaymentsReceivedPages';
import { PaymentReceivesApplication } from '@/services/Sales/PaymentReceived/PaymentReceivedApplication';
import DynamicListingService from '@/services/DynamicListing/DynamicListService';
import { PaymentReceivesApplication } from '@/services/Sales/PaymentReceives/PaymentReceivesApplication';
import CheckPolicies from '@/api/middleware/CheckPolicies';
import { ServiceError } from '@/exceptions';
import { ACCEPT_TYPE } from '@/interfaces/Http';
@@ -22,7 +22,7 @@ export default class PaymentReceivesController extends BaseController {
private paymentReceiveApplication: PaymentReceivesApplication;
@Inject()
private PaymentReceivesPages: PaymentReceivesPages;
private PaymentsReceivedPages: PaymentsReceivedPages;
@Inject()
private dynamicListService: DynamicListingService;
@@ -154,8 +154,8 @@ export default class PaymentReceivesController extends BaseController {
check('payment_date').exists(),
check('reference_no').optional(),
check('deposit_account_id').exists().isNumeric().toInt(),
check('payment_receive_no').optional({ nullable: true }).trim().escape(),
check('statement').optional().trim().escape(),
check('payment_receive_no').optional({ nullable: true }).trim(),
check('statement').optional().trim(),
check('branch_id').optional({ nullable: true }).isNumeric().toInt(),
@@ -176,7 +176,6 @@ export default class PaymentReceivesController extends BaseController {
private get validatePaymentReceiveList(): ValidationChain[] {
return [
query('stringified_filter_roles').optional().isJSON(),
query('view_slug').optional({ nullable: true }).isString().trim(),
query('column_sort_by').optional(),
@@ -230,7 +229,7 @@ export default class PaymentReceivesController extends BaseController {
try {
const storedPaymentReceive =
await this.paymentReceiveApplication.createPaymentReceive(
await this.paymentReceiveApplication.createPaymentReceived(
tenantId,
paymentReceive,
user
@@ -377,7 +376,7 @@ export default class PaymentReceivesController extends BaseController {
const { customerId } = this.matchedQueryData(req);
try {
const entries = await this.PaymentReceivesPages.getNewPageEntries(
const entries = await this.PaymentsReceivedPages.getNewPageEntries(
tenantId,
customerId
);
@@ -405,7 +404,7 @@ export default class PaymentReceivesController extends BaseController {
try {
const { paymentReceive, entries } =
await this.PaymentReceivesPages.getPaymentReceiveEditPage(
await this.PaymentsReceivedPages.getPaymentReceiveEditPage(
tenantId,
paymentReceiveId,
user

View File

@@ -155,7 +155,7 @@ export default class SalesEstimatesController extends BaseController {
check('estimate_date').exists().isISO8601().toDate(),
check('expiration_date').exists().isISO8601().toDate(),
check('reference').optional(),
check('estimate_number').optional().trim().escape(),
check('estimate_number').optional().trim(),
check('delivered').default(false).isBoolean().toBoolean(),
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
@@ -170,8 +170,7 @@ export default class SalesEstimatesController extends BaseController {
check('entries.*.rate').exists().isNumeric().toFloat(),
check('entries.*.description')
.optional({ nullable: true })
.trim()
.escape(),
.trim(),
check('entries.*.discount')
.optional({ nullable: true })
.isNumeric()
@@ -181,9 +180,9 @@ export default class SalesEstimatesController extends BaseController {
.isNumeric()
.toInt(),
check('note').optional().trim().escape(),
check('terms_conditions').optional().trim().escape(),
check('send_to_email').optional().trim().escape(),
check('note').optional().trim(),
check('terms_conditions').optional().trim(),
check('send_to_email').optional().trim(),
check('attachments').isArray().optional(),
check('attachments.*.key').exists().isString(),

View File

@@ -200,12 +200,12 @@ export default class SaleInvoicesController extends BaseController {
check('customer_id').exists().isNumeric().toInt(),
check('invoice_date').exists().isISO8601().toDate(),
check('due_date').exists().isISO8601().toDate(),
check('invoice_no').optional().trim().escape(),
check('reference_no').optional().trim().escape(),
check('invoice_no').optional().trim(),
check('reference_no').optional().trim(),
check('delivered').default(false).isBoolean().toBoolean(),
check('invoice_message').optional().trim().escape(),
check('terms_conditions').optional().trim().escape(),
check('invoice_message').optional().trim(),
check('terms_conditions').optional().trim(),
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
@@ -226,12 +226,10 @@ export default class SaleInvoicesController extends BaseController {
.toFloat(),
check('entries.*.description')
.optional({ nullable: true })
.trim()
.escape(),
.trim(),
check('entries.*.tax_code')
.optional({ nullable: true })
.trim()
.escape()
.isString(),
check('entries.*.tax_rate_id')
.optional({ nullable: true })

View File

@@ -130,8 +130,8 @@ export default class SalesReceiptsController extends BaseController {
check('deposit_account_id').exists().isNumeric().toInt(),
check('receipt_date').exists().isISO8601(),
check('receipt_number').optional().trim().escape(),
check('reference_no').optional().trim().escape(),
check('receipt_number').optional().trim(),
check('reference_no').optional().trim(),
check('closed').default(false).isBoolean().toBoolean(),
check('warehouse_id').optional({ nullable: true }).isNumeric().toInt(),
@@ -150,14 +150,13 @@ export default class SalesReceiptsController extends BaseController {
.toInt(),
check('entries.*.description')
.optional({ nullable: true })
.trim()
.escape(),
.trim(),
check('entries.*.warehouse_id')
.optional({ nullable: true })
.isNumeric()
.toInt(),
check('receipt_message').optional().trim().escape(),
check('statement').optional().trim().escape(),
check('receipt_message').optional().trim(),
check('statement').optional().trim(),
check('attachments').isArray().optional(),
check('attachments.*.key').exists().isString(),
];

View File

@@ -52,10 +52,7 @@ export default class SettingsController extends BaseController {
* Retrieve the application options from the storage.
*/
private get getSettingsSchema() {
return [
query('key').optional().trim().escape(),
query('group').optional().trim().escape(),
];
return [query('key').optional().trim(), query('group').optional().trim()];
}
/**

View File

@@ -8,6 +8,7 @@ import SubscriptionService from '@/services/Subscription/SubscriptionService';
import asyncMiddleware from '@/api/middleware/asyncMiddleware';
import BaseController from '../BaseController';
import { LemonSqueezyService } from '@/services/Subscription/LemonSqueezyService';
import { SubscriptionApplication } from '@/services/Subscription/SubscriptionApplication';
@Service()
export class SubscriptionController extends BaseController {
@@ -17,6 +18,9 @@ export class SubscriptionController extends BaseController {
@Inject()
private lemonSqueezyService: LemonSqueezyService;
@Inject()
private subscriptionApp: SubscriptionApplication;
/**
* Router constructor.
*/
@@ -33,6 +37,14 @@ export class SubscriptionController extends BaseController {
this.validationResult,
this.getCheckoutUrl.bind(this)
);
router.post('/cancel', asyncMiddleware(this.cancelSubscription.bind(this)));
router.post('/resume', asyncMiddleware(this.resumeSubscription.bind(this)));
router.post(
'/change',
[body('variant_id').exists().trim()],
this.validationResult,
asyncMiddleware(this.changeSubscriptionPlan.bind(this))
);
router.get('/', asyncMiddleware(this.getSubscriptions.bind(this)));
return router;
@@ -85,4 +97,84 @@ export class SubscriptionController extends BaseController {
next(error);
}
}
/**
* Cancels the subscription of the current organization.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Promise<Response|null>}
*/
private async cancelSubscription(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
try {
await this.subscriptionApp.cancelSubscription(tenantId);
return res.status(200).send({
status: 200,
message: 'The organization subscription has been canceled.',
});
} catch (error) {
next(error);
}
}
/**
* Resumes the subscription of the current organization.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Promise<Response | null>}
*/
private async resumeSubscription(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
try {
await this.subscriptionApp.resumeSubscription(tenantId);
return res.status(200).send({
status: 200,
message: 'The organization subscription has been resumed.',
});
} catch (error) {
next(error);
}
}
/**
* Changes the main subscription plan of the current organization.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
* @returns {Promise<Response | null>}
*/
public async changeSubscriptionPlan(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const body = this.matchedBodyData(req);
try {
await this.subscriptionApp.changeSubscriptionPlan(
tenantId,
body.variantId
);
return res.status(200).send({
message: 'The subscription plan has been changed.',
});
} catch (error) {
next(error);
}
}
}

View File

@@ -32,7 +32,7 @@ export default class ViewsController extends BaseController {
* Custom views list validation schema.
*/
get viewsListSchemaValidation() {
return [param('resource_model').exists().trim().escape()];
return [param('resource_model').exists().trim()];
}
/**

View File

@@ -35,7 +35,7 @@ export class Webhooks extends BaseController {
*/
public async lemonWebhooks(req: Request, res: Response, next: NextFunction) {
const data = req.body;
const signature = req.headers['x-signature'] ?? '';
const signature = req.headers['x-signature'] as string ?? '';
const rawBody = req.rawBody;
try {

View File

@@ -63,6 +63,7 @@ import { BankingController } from './controllers/Banking/BankingController';
import { Webhooks } from './controllers/Webhooks/Webhooks';
import { ExportController } from './controllers/Export/ExportController';
import { AttachmentsController } from './controllers/Attachments/AttachmentsController';
import { OneClickDemoController } from './controllers/OneClickDemo/OneClickDemoController';
export default () => {
const app = Router();
@@ -80,6 +81,7 @@ export default () => {
app.use('/jobs', Container.get(Jobs).router());
app.use('/account', Container.get(Account).router());
app.use('/webhooks', Container.get(Webhooks).router());
app.use('/demo', Container.get(OneClickDemoController).router())
// - Dashboard routes.
// ---------------------------

View File

@@ -1,11 +1,21 @@
import { Request, Response, NextFunction } from 'express';
import { Container } from 'typedi';
import { Request, Response, NextFunction } from 'express';
import SettingsStore from '@/services/Settings/SettingsStore';
export default async (req: Request, res: Response, next: NextFunction) => {
const { tenantId } = req.user;
const Logger = Container.get('logger');
const settings = await initializeTenantSettings(tenantId);
req.settings = settings;
res.on('finish', async () => {
await settings.save();
});
next();
}
export const initializeTenantSettings = async (tenantId: number) => {
const tenantContainer = Container.of(`tenant-${tenantId}`);
if (tenantContainer && !tenantContainer.has('settings')) {
@@ -18,10 +28,5 @@ export default async (req: Request, res: Response, next: NextFunction) => {
await settings.load();
req.settings = settings;
res.on('finish', async () => {
await settings.save();
});
next();
return settings;
}

View File

@@ -1,6 +1,8 @@
import { Container } from 'typedi';
import { Request, Response, NextFunction } from 'express';
const SupportedMethods = ['POST', 'PUT'];
export default (subscriptionSlug = 'main') =>
async (req: Request, res: Response, next: NextFunction) => {
const { tenant, tenantId } = req;
@@ -19,8 +21,10 @@ export default (subscriptionSlug = 'main') =>
errors: [{ type: 'TENANT.HAS.NO.SUBSCRIPTION' }],
});
}
// Validate in case the subscription is inactive.
else if (subscription.inactive()) {
const isMethodSupported = SupportedMethods.includes(req.method);
const isSubscriptionInactive = subscription.inactive();
if (isMethodSupported && isSubscriptionInactive) {
return res.boom.badRequest(null, {
errors: [{ type: 'ORGANIZATION.SUBSCRIPTION.INACTIVE' }],
});

View File

@@ -4,6 +4,7 @@ import { Request } from 'express';
import TenancyService from '@/services/Tenancy/TenancyService';
import TenantsManagerService from '@/services/Tenancy/TenantsManager';
import rtlDetect from 'rtl-detect';
import { Tenant } from '@/system/models';
export default (req: Request, tenant: ITenant) => {
const { id: tenantId, organizationId } = tenant;
@@ -16,7 +17,7 @@ export default (req: Request, tenant: ITenant) => {
const tenantContainer = tenantServices.tenantContainer(tenantId);
tenantContainer.set('i18n', injectI18nUtils(req));
tenantContainer.set('i18n', injectI18nUtils());
const knexInstance = tenantServices.knex(tenantId);
const models = tenantServices.models(tenantId);
@@ -33,14 +34,35 @@ export default (req: Request, tenant: ITenant) => {
};
export const injectI18nUtils = (req) => {
const locale = req.getLocale();
const globalI18n = Container.get('i18n');
const locale = globalI18n.getLocale();
const direction = rtlDetect.getLangDir(locale);
return {
locale,
__: req.__,
__: globalI18n.__,
direction,
isRtl: direction === 'rtl',
isLtr: direction === 'ltr',
};
};
export const initalizeTenantServices = async (tenantId: number) => {
const tenant = await Tenant.query()
.findById(tenantId)
.withGraphFetched('metadata');
const tenantServices = Container.get(TenancyService);
const tenantsManager = Container.get(TenantsManagerService);
// Initialize the knex instance.
tenantsManager.setupKnexInstance(tenant);
const tenantContainer = tenantServices.tenantContainer(tenantId);
tenantContainer.set('i18n', injectI18nUtils());
tenantServices.knex(tenantId);
tenantServices.models(tenantId);
tenantServices.repositories(tenantId);
tenantServices.cache(tenantId);
};

View File

@@ -32,7 +32,7 @@ module.exports = {
*/
tenant: {
db_client: process.env.TENANT_DB_CLIENT || process.env.DB_CLIENT || 'mysql',
db_name_prefix: process.env.TENANT_DB_NAME_PERFIX,
db_name_prefix: process.env.TENANT_DB_NAME_PERFIX || 'bigcapital_tenant_',
db_host: process.env.TENANT_DB_HOST || process.env.DB_HOST,
db_user: process.env.TENANT_DB_USER || process.env.DB_USER,
db_password: process.env.TENANT_DB_PASSWORD || process.env.DB_PASSWORD,
@@ -236,9 +236,29 @@ module.exports = {
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
endpoint: process.env.S3_ENDPOINT,
bucket: process.env.S3_BUCKET || 'bigcapital-documents',
forcePathStyle: parseBoolean(
defaultTo(process.env.S3_FORCE_PATH_STYLE, false),
false
),
},
loops: {
apiKey: process.env.LOOPS_API_KEY,
},
/**
* One-click demo accounts.
*/
oneClickDemoAccounts: {
enable: parseBoolean(process.env.ONE_CLICK_DEMO_ACCOUNTS, false),
demoUrl: process.env.ONE_CLICK_DEMO_ACCOUNTS_URL || '',
},
/**
* PostHog
*/
posthog: {
apiKey: process.env.POSTHOG_API_KEY,
host: process.env.POSTHOG_HOST
}
};

View File

@@ -0,0 +1,79 @@
export const SALE_INVOICE_CREATED = 'Sale invoice created';
export const SALE_INVOICE_EDITED = 'Sale invoice d';
export const SALE_INVOICE_DELETED = 'Sale invoice deleted';
export const SALE_INVOICE_MAIL_DELIVERED = 'Sale invoice mail delivered';
export const SALE_ESTIMATE_CREATED = 'Sale estimate created';
export const SALE_ESTIMATE_EDITED = 'Sale estimate edited';
export const SALE_ESTIMATE_DELETED = 'Sale estimate deleted';
export const PAYMENT_RECEIVED_CREATED = 'Payment received created';
export const PAYMENT_RECEIVED_EDITED = 'payment received edited';
export const PAYMENT_RECEIVED_DELETED = 'Payment received deleted';
export const BILL_CREATED = 'Bill created';
export const BILL_EDITED = 'Bill edited';
export const BILL_DELETED = 'Bill deleted';
export const PAYMENT_MADE_CREATED = 'Payment made created';
export const PAYMENT_MADE_EDITED = 'Payment made edited';
export const PAYMENT_MADE_DELETED = 'Payment made deleted';
export const EXPENSE_CREATED = 'Expense created';
export const EXPENSE_EDITED = 'Expense edited';
export const EXPENSE_DELETED = 'Expense deleted';
export const ACCOUNT_CREATED = 'Account created';
export const ACCOUNT_EDITED = 'Account Edited';
export const ACCOUNT_DELETED = 'Account deleted';
export const ITEM_EVENT_CREATED = 'Item created';
export const ITEM_EVENT_EDITED = 'Item edited';
export const ITEM_EVENT_DELETED = 'Item deleted';
export const AUTH_SIGNED_UP = 'Auth Signed-up';
export const AUTH_RESET_PASSWORD = 'Auth reset password';
export const SUBSCRIPTION_CANCELLED = 'Subscription cancelled';
export const SUBSCRIPTION_RESUMED = 'Subscription resumed';
export const SUBSCRIPTION_PLAN_CHANGED = 'Subscription plan changed';
export const CUSTOMER_CREATED = 'Customer created';
export const CUSTOMER_EDITED = 'Customer edited';
export const CUSTOMER_DELETED = 'Customer deleted';
export const VENDOR_CREATED = 'Vendor created';
export const VENDOR_EDITED = 'Vendor edited';
export const VENDOR_DELETED = 'Vendor deleted';
export const TRANSACTIONS_LOCKING_LOCKED = 'Transactions locking locked';
export const TRANSACTIONS_LOCKING_LOCKING_CANCELLED =
'Transactions locking cancelled';
export const TRANSACTIONS_LOCKING_PARTIALLY_UNLOCKED =
'Transactions locking partially unlocked';
export const TRANSACTIONS_LOCKING_PARTIALLY_UNLOCK_CANCELLED =
'Transactions locking partially unlock cancelled';
export const BANK_TRANSACTION_MATCHED = 'Bank transaction matching deleted';
export const BANK_TRANSACTION_EXCLUDED = 'Bank transaction excluded';
export const BANK_TRANSACTION_CATEGORIZED = 'Bank transaction categorized';
export const BANK_TRANSACTION_UNCATEGORIZED = 'Bank transaction uncategorized';
export const BANK_ACCOUNT_DISCONNECTED = 'Bank account disconnected';
export const MANUAL_JOURNAL_CREATED = 'Manual journal created';
export const MANUAL_JOURNAL_EDITED = 'Manual journal edited';
export const MANUAL_JOURNAL_DELETED = 'Manual journal deleted';
export const MANUAL_JOURNAL_PUBLISHED = 'Manual journal published';
export const BANK_RULE_CREATED = 'Bank rule created';
export const BANK_RULE_EDITED = 'Bank rule edited';
export const BANK_RULE_DELETED = 'Bank rule deleted';
// # Event Groups
export const ACCOUNT_GROUP = 'Account';
export const ITEM_GROUP = 'Item';
export const AUTH_GROUP = 'Auth';
export const SALE_GROUP = 'Sale';
export const PAYMENT_GROUP = 'Payment';
export const BILL_GROUP = 'Bill';
export const EXPENSE_GROUP = 'Expense';

View File

@@ -0,0 +1,11 @@
exports.up = function (knex) {
return knex.schema.table('accounts', (table) => {
table.string('plaid_item_id').nullable();
});
};
exports.down = function (knex) {
return knex.schema.table('accounts', (table) => {
table.dropColumn('plaid_item_id');
});
};

View File

@@ -0,0 +1,19 @@
exports.up = function (knex) {
return knex.schema
.table('accounts', (table) => {
table
.boolean('is_syncing_owner')
.defaultTo(false)
.after('is_feeds_active');
})
.then(() => {
return knex('accounts')
.whereNotNull('plaid_item_id')
.orWhereNotNull('plaid_account_id')
.update('is_syncing_owner', true);
});
};
exports.down = function (knex) {
table.dropColumn('is_syncing_owner');
};

View File

@@ -0,0 +1,18 @@
// This migration changes the precision of the tax_amount_withheld column in the bills and sales_invoices tables from 8, 2 to 13, 2.
// This migration is necessary to allow tax_amount_withheld filed to store values bigger than 999,999.99.
exports.up = function(knex) {
return knex.schema.alterTable('bills', function (table) {
table.decimal('tax_amount_withheld', 13, 2).alter();
}).alterTable('sales_invoices', function (table) {
table.decimal('tax_amount_withheld', 13, 2).alter();
});
};
exports.down = function(knex) {
return knex.schema.alterTable('bills', function (table) {
table.decimal('tax_amount_withheld', 8, 2).alter();
}).alterTable('sales_invoices', function (table) {
table.decimal('tax_amount_withheld', 8, 2).alter();
});
};

View File

@@ -0,0 +1,11 @@
exports.up = function (knex) {
return knex.schema.table('plaid_items', (table) => {
table.datetime('paused_at');
});
};
exports.down = function (knex) {
return knex.schema.table('plaid_items', (table) => {
table.dropColumn('paused_at');
});
};

View File

@@ -0,0 +1,13 @@
exports.up = function (knex) {
return knex.schema.table('uncategorized_cashflow_transactions', (table) => {
table.boolean('pending').defaultTo(false);
table.string('pending_plaid_transaction_id').nullable();
});
};
exports.down = function (knex) {
return knex.schema.table('uncategorized_cashflow_transactions', (table) => {
table.dropColumn('pending');
table.dropColumn('pending_plaid_transaction_id');
});
};

View File

@@ -15,6 +15,7 @@ export default class SeedSettings extends TenantSeeder {
// Manual journals settings.
{ group: 'manual_journals', key: 'next_number', value: '00001' },
{ group: 'manual_journals', key: 'number_prefix', value: 'J-' },
{ group: 'manual_journals', key: 'auto_increment', value: true },
// Sale invoices settings.

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

@@ -15,6 +15,7 @@ export interface IAccountDTO {
export interface IAccountCreateDTO extends IAccountDTO {
currencyCode?: string;
plaidAccountId?: string;
plaidItemId?: string;
}
export interface IAccountEditDTO extends IAccountDTO {}
@@ -37,6 +38,8 @@ export interface IAccount {
accountNormal: string;
accountParentType: string;
bankBalance: string;
plaidItemId: number | null
lastFeedsUpdatedAt: Date;
}
export enum AccountNormal {

View File

@@ -7,7 +7,6 @@ export interface IRegisterDTO {
lastName: string;
email: string;
password: string;
organizationName: string;
}
export interface ILoginDTO {
@@ -77,6 +76,10 @@ export interface IAuthSendedResetPassword {
export interface IAuthGetMetaPOJO {
signupDisabled: boolean;
oneClickDemo: {
enable: boolean;
demoUrl: string;
};
}
export interface IAuthSignUpVerifingEventPayload {

View File

@@ -99,6 +99,7 @@ export interface IBillsFilter extends IDynamicListFilterDTO {
stringifiedFilterRoles?: string;
page: number;
pageSize: number;
filterQuery?: (q: any) => void;
}
export interface IBillsService {

View File

@@ -236,6 +236,7 @@ export interface ICashflowTransactionSchema {
export interface ICashflowTransactionInput extends ICashflowTransactionSchema {}
export interface ICategorizeCashflowTransactioDTO {
date: Date;
creditAccountId: number;
referenceNo: string;
transactionNumber: string;
@@ -267,6 +268,8 @@ export interface CreateUncategorizedTransactionDTO {
description?: string;
referenceNo?: string | null;
plaidTransactionId?: string | null;
pending?: boolean;
pendingPlaidTransactionId?: string | null;
batch?: string;
}
@@ -282,3 +285,17 @@ export interface IUncategorizedTransactionCreatedEventPayload {
createUncategorizedTransactionDTO: CreateUncategorizedTransactionDTO;
trx: Knex.Transaction;
}
export interface IPendingTransactionRemovingEventPayload {
tenantId: number;
uncategorizedTransactionId: number;
pendingTransaction: IUncategorizedCashflowTransaction;
trx?: Knex.Transaction;
}
export interface IPendingTransactionRemovedEventPayload {
tenantId: number;
uncategorizedTransactionId: number;
pendingTransaction: IUncategorizedCashflowTransaction;
trx?: Knex.Transaction;
}

View File

@@ -130,20 +130,23 @@ export interface ICommandCashflowDeletedPayload {
export interface ICashflowTransactionCategorizedPayload {
tenantId: number;
uncategorizedTransaction: any;
uncategorizedTransactions: Array<IUncategorizedCashflowTransaction>;
cashflowTransaction: ICashflowTransaction;
oldUncategorizedTransactions: Array<IUncategorizedCashflowTransaction>;
categorizeDTO: any;
trx: Knex.Transaction;
}
export interface ICashflowTransactionUncategorizingPayload {
tenantId: number;
uncategorizedTransaction: IUncategorizedCashflowTransaction;
uncategorizedTransactionId: number;
oldUncategorizedTransactions: Array<IUncategorizedCashflowTransaction>;
trx: Knex.Transaction;
}
export interface ICashflowTransactionUncategorizedPayload {
tenantId: number;
uncategorizedTransaction: IUncategorizedCashflowTransaction;
oldUncategorizedTransaction: IUncategorizedCashflowTransaction;
uncategorizedTransactionId: number;
uncategorizedTransactions: Array<IUncategorizedCashflowTransaction>;
oldUncategorizedTransactions: Array<IUncategorizedCashflowTransaction>;
trx: Knex.Transaction;
}
@@ -164,11 +167,18 @@ export interface CategorizeTransactionAsExpenseDTO {
export interface IGetUncategorizedTransactionsQuery {
page?: number;
pageSize?: number;
minDate?: Date;
maxDate?: Date;
minAmount?: number;
maxAmount?: number;
}
export interface IGetRecognizedTransactionsQuery {
page?: number;
pageSize?: number;
accountId?: number;
}
minDate?: Date;
maxDate?: Date;
minAmount?: number;
maxAmount?: number;
}

View File

@@ -241,6 +241,7 @@ export interface ICustomerEventCreatingPayload {
trx: Knex.Transaction;
}
export interface ICustomerEventEditedPayload {
tenantId: number
customerId: number;
customer: ICustomer;
trx: Knex.Transaction;

View File

@@ -1,10 +1,10 @@
import { Knex } from 'knex';
import { IDynamicListFilter, IItemEntry, IVendorCredit } from '@/interfaces';
import { IDynamicListFilter, IItemEntry } from '@/interfaces';
import { ILedgerEntry } from './Ledger';
import { AttachmentLinkDTO } from './Attachments';
export interface ICreditNoteEntryNewDTO {
index: number;
index?: number;
itemId: number;
rate: number;
quantity: number;
@@ -22,7 +22,7 @@ export interface ICreditNoteNewDTO {
entries: ICreditNoteEntryNewDTO[];
branchId?: number;
warehouseId?: number;
attachments?: AttachmentLinkDTO[]
attachments?: AttachmentLinkDTO[];
}
export interface ICreditNoteEditDTO {
@@ -35,7 +35,7 @@ export interface ICreditNoteEditDTO {
entries: ICreditNoteEntryNewDTO[];
branchId?: number;
warehouseId?: number;
attachments?: AttachmentLinkDTO[]
attachments?: AttachmentLinkDTO[];
}
export interface ICreditNoteEntry extends IItemEntry {}
@@ -61,7 +61,7 @@ export interface ICreditNote {
localAmount?: number;
branchId?: number;
warehouseId: number;
createdAt?: Date,
createdAt?: Date;
}
export enum CreditNoteAction {
@@ -130,7 +130,9 @@ export interface ICreditNoteOpenedPayload {
trx: Knex.Transaction;
}
export interface ICreditNotesQueryDTO {}
export interface ICreditNotesQueryDTO {
filterQuery?: (query: any) => void;
}
export interface ICreditNotesQueryDTO extends IDynamicListFilter {
page: number;

View File

@@ -17,6 +17,7 @@ export interface IExpensesFilter {
columnSortBy: string;
sortOrder: string;
viewSlug?: string;
filterQuery?: (query: any) => void;
}
export interface IExpense {

View File

@@ -30,7 +30,7 @@ export interface IGeneralLedgerSheetAccountTransaction {
currencyCode: string;
note?: string;
transactionType?: string;
transactionTypeFormatted: string;
transactionNumber: string;
referenceId?: number;

View File

@@ -3,6 +3,6 @@ import { ImportFilePreviewPOJO } from "@/services/Import/interfaces";
export interface IImportFileCommitedEventPayload {
tenantId: number;
importId: number;
importId: string;
meta: ImportFilePreviewPOJO;
}

View File

@@ -48,6 +48,7 @@ export interface IItemEntry {
export interface IItemEntryDTO {
id?: number;
index?: number;
itemId: number;
landedCost?: boolean;
warehouseId?: number;

View File

@@ -151,6 +151,7 @@ export interface IModelMetaFieldCommon2 {
importHint?: string;
order?: number;
unique?: number;
features?: Array<any>
}
export interface IModelMetaRelationField2 {

View File

@@ -8,7 +8,7 @@ import { ILedgerEntry } from './Ledger';
import { ISaleInvoice } from './SaleInvoice';
import { AttachmentLinkDTO } from './Attachments';
export interface IPaymentReceive {
export interface IPaymentReceived {
id?: number;
customerId: number;
paymentDate: Date;
@@ -19,14 +19,14 @@ export interface IPaymentReceive {
depositAccountId: number;
paymentReceiveNo: string;
statement: string;
entries: IPaymentReceiveEntry[];
entries: IPaymentReceivedEntry[];
userId: number;
createdAt: Date;
updatedAt: Date;
localAmount?: number;
branchId?: number;
}
export interface IPaymentReceiveCreateDTO {
export interface IPaymentReceivedCreateDTO {
customerId: number;
paymentDate: Date;
amount: number;
@@ -35,13 +35,13 @@ export interface IPaymentReceiveCreateDTO {
depositAccountId: number;
paymentReceiveNo?: string;
statement: string;
entries: IPaymentReceiveEntryDTO[];
entries: IPaymentReceivedEntryDTO[];
branchId?: number;
attachments?: AttachmentLinkDTO[];
}
export interface IPaymentReceiveEditDTO {
export interface IPaymentReceivedEditDTO {
customerId: number;
paymentDate: Date;
amount: number;
@@ -50,12 +50,12 @@ export interface IPaymentReceiveEditDTO {
depositAccountId: number;
paymentReceiveNo?: string;
statement: string;
entries: IPaymentReceiveEntryDTO[];
entries: IPaymentReceivedEntryDTO[];
branchId?: number;
attachments?: AttachmentLinkDTO[];
}
export interface IPaymentReceiveEntry {
export interface IPaymentReceivedEntry {
id?: number;
paymentReceiveId: number;
invoiceId: number;
@@ -64,16 +64,17 @@ export interface IPaymentReceiveEntry {
invoice?: ISaleInvoice;
}
export interface IPaymentReceiveEntryDTO {
export interface IPaymentReceivedEntryDTO {
id?: number;
index: number;
paymentReceiveId: number;
index?: number;
paymentReceiveId?: number;
invoiceId: number;
paymentAmount: number;
}
export interface IPaymentReceivesFilter extends IDynamicListFilterDTO {
export interface IPaymentsReceivedFilter extends IDynamicListFilterDTO {
stringifiedFilterRoles?: string;
filterQuery?: (trx: Knex.Transaction) => void;
}
export interface IPaymentReceivePageEntry {
@@ -88,65 +89,65 @@ export interface IPaymentReceivePageEntry {
date: Date | string;
}
export interface IPaymentReceiveEditPage {
paymentReceive: IPaymentReceive;
export interface IPaymentReceivedEditPage {
paymentReceive: IPaymentReceived;
entries: IPaymentReceivePageEntry[];
}
export interface IPaymentsReceiveService {
export interface IPaymentsReceivedService {
validateCustomerHasNoPayments(
tenantId: number,
customerId: number
): Promise<void>;
}
export interface IPaymentReceiveSmsDetails {
export interface IPaymentReceivedSmsDetails {
customerName: string;
customerPhoneNumber: string;
smsMessage: string;
}
export interface IPaymentReceiveCreatingPayload {
export interface IPaymentReceivedCreatingPayload {
tenantId: number;
paymentReceiveDTO: IPaymentReceiveCreateDTO;
paymentReceiveDTO: IPaymentReceivedCreateDTO;
trx: Knex.Transaction;
}
export interface IPaymentReceiveCreatedPayload {
export interface IPaymentReceivedCreatedPayload {
tenantId: number;
paymentReceive: IPaymentReceive;
paymentReceive: IPaymentReceived;
paymentReceiveId: number;
authorizedUser: ISystemUser;
paymentReceiveDTO: IPaymentReceiveCreateDTO;
paymentReceiveDTO: IPaymentReceivedCreateDTO;
trx: Knex.Transaction;
}
export interface IPaymentReceiveEditedPayload {
export interface IPaymentReceivedEditedPayload {
tenantId: number;
paymentReceiveId: number;
paymentReceive: IPaymentReceive;
oldPaymentReceive: IPaymentReceive;
paymentReceiveDTO: IPaymentReceiveEditDTO;
paymentReceive: IPaymentReceived;
oldPaymentReceive: IPaymentReceived;
paymentReceiveDTO: IPaymentReceivedEditDTO;
authorizedUser: ISystemUser;
trx: Knex.Transaction;
}
export interface IPaymentReceiveEditingPayload {
export interface IPaymentReceivedEditingPayload {
tenantId: number;
oldPaymentReceive: IPaymentReceive;
paymentReceiveDTO: IPaymentReceiveEditDTO;
oldPaymentReceive: IPaymentReceived;
paymentReceiveDTO: IPaymentReceivedEditDTO;
trx: Knex.Transaction;
}
export interface IPaymentReceiveDeletingPayload {
export interface IPaymentReceivedDeletingPayload {
tenantId: number;
oldPaymentReceive: IPaymentReceive;
oldPaymentReceive: IPaymentReceived;
trx: Knex.Transaction;
}
export interface IPaymentReceiveDeletedPayload {
export interface IPaymentReceivedDeletedPayload {
tenantId: number;
paymentReceiveId: number;
oldPaymentReceive: IPaymentReceive;
oldPaymentReceive: IPaymentReceived;
authorizedUser: ISystemUser;
trx: Knex.Transaction;
}

View File

@@ -44,6 +44,7 @@ export interface ISaleEstimateDTO {
export interface ISalesEstimatesFilter extends IDynamicListFilterDTO {
stringifiedFilterRoles?: string;
filterQuery?: (q: any) => void;
}
export interface ISalesEstimatesService {

View File

@@ -79,6 +79,7 @@ export interface ISalesInvoicesFilter extends IDynamicListFilter {
page: number;
pageSize: number;
searchKeyword?: string;
filterQuery?: (q: Knex.QueryBuilder) => void;
}
export interface ISalesInvoicesService {

View File

@@ -29,7 +29,9 @@ export interface ISaleReceipt {
entries?: IItemEntry[];
}
export interface ISalesReceiptsFilter {}
export interface ISalesReceiptsFilter {
filterQuery?: (query: any) => void;
}
export interface ISaleReceiptDTO {
customerId: number;

View File

@@ -33,3 +33,7 @@ export interface IOrganizationBuildEventPayload {
buildDTO: IOrganizationBuildDTO;
systemUser: ISystemUser;
}
export interface IOrganizationBuiltEventPayload {
tenantId: number;
}

View File

@@ -0,0 +1,8 @@
export interface SubscriptionPayload {
lemonSqueezyId?: string;
}
export enum SubscriptionPaymentStatus {
Succeed = 'succeed',
Failed = 'failed',
}

View File

@@ -51,5 +51,4 @@ export interface ISystemService {
cache();
repositories();
knex();
dbManager();
}

View File

@@ -106,6 +106,7 @@ export interface IVendorCreditsQueryDTO extends IDynamicListFilter {
page: number;
pageSize: number;
searchKeyword?: string;
filterQuery?: (q: any) => void;
}
export interface IVendorCreditEditingPayload {

View File

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

View File

@@ -21,7 +21,7 @@ export default class ComputeItemCostJob {
agenda.define(
'compute-item-cost',
{ priority: 'high', concurrency: 1 },
{ priority: 'high', concurrency: 20 },
this.handler.bind(this)
);
this.agenda.on('start:compute-item-cost', this.onJobStart.bind(this));

View File

@@ -8,7 +8,7 @@ export default class OrganizationSetupJob {
constructor(agenda) {
agenda.define(
'organization-setup',
{ priority: 'high', concurrency: 1 },
{ priority: 'high', concurrency: 20 },
this.handler
);
}

View File

@@ -15,7 +15,7 @@ export default class WriteInvoicesJournalEntries {
agenda.define(
eventName,
{ priority: 'normal', concurrency: 1 },
{ priority: 'normal', concurrency: 20 },
this.handler.bind(this)
);
agenda.on(`complete:${eventName}`, this.onJobCompleted.bind(this));

View File

@@ -1,69 +1,12 @@
import { forEach } from 'lodash';
import { Configuration, PlaidApi, PlaidEnvironments } from 'plaid';
import { createPlaidApiEvent } from './PlaidApiEventsDBSync';
import config from '@/config';
const OPTIONS = { clientApp: 'Plaid-Pattern' };
// We want to log requests to / responses from the Plaid API (via the Plaid client), as this data
// can be useful for troubleshooting.
/**
* Logging function for Plaid client methods that use an access_token as an argument. Associates
* the Plaid API event log entry with the item and user the request is for.
*
* @param {string} clientMethod the name of the Plaid client method called.
* @param {Array} clientMethodArgs the arguments passed to the Plaid client method.
* @param {Object} response the response from the Plaid client.
*/
const defaultLogger = async (clientMethod, clientMethodArgs, response) => {
const accessToken = clientMethodArgs[0].access_token;
// const { id: itemId, user_id: userId } = await retrieveItemByPlaidAccessToken(
// accessToken
// );
// await createPlaidApiEvent(1, 1, clientMethod, clientMethodArgs, response);
// console.log(response);
};
/**
* Logging function for Plaid client methods that do not use access_token as an argument. These
* Plaid API event log entries will not be associated with an item or user.
*
* @param {string} clientMethod the name of the Plaid client method called.
* @param {Array} clientMethodArgs the arguments passed to the Plaid client method.
* @param {Object} response the response from the Plaid client.
*/
const noAccessTokenLogger = async (
clientMethod,
clientMethodArgs,
response
) => {
// console.log(response);
// await createPlaidApiEvent(
// undefined,
// undefined,
// clientMethod,
// clientMethodArgs,
// response
// );
};
// Plaid client methods used in this app, mapped to their appropriate logging functions.
const clientMethodLoggingFns = {
accountsGet: defaultLogger,
institutionsGet: noAccessTokenLogger,
institutionsGetById: noAccessTokenLogger,
itemPublicTokenExchange: noAccessTokenLogger,
itemRemove: defaultLogger,
linkTokenCreate: noAccessTokenLogger,
transactionsSync: defaultLogger,
sandboxItemResetLogin: defaultLogger,
};
// Wrapper for the Plaid client. This allows us to easily log data for all Plaid client requests.
export class PlaidClientWrapper {
constructor() {
private static instance: PlaidClientWrapper;
private client: PlaidApi;
private constructor() {
// Initialize the Plaid client.
const configuration = new Configuration({
basePath: PlaidEnvironments[config.plaid.env],
@@ -75,26 +18,13 @@ export class PlaidClientWrapper {
},
},
});
this.client = new PlaidApi(configuration);
// Wrap the Plaid client methods to add a logging function.
forEach(clientMethodLoggingFns, (logFn, method) => {
this[method] = this.createWrappedClientMethod(method, logFn);
});
}
// Allows us to log API request data for troubleshooting purposes.
createWrappedClientMethod(clientMethod, log) {
return async (...args) => {
try {
const res = await this.client[clientMethod](...args);
await log(clientMethod, args, res);
return res;
} catch (err) {
await log(clientMethod, args, err?.response?.data);
throw err;
}
};
public static getClient(): PlaidApi {
if (!PlaidClientWrapper.instance) {
PlaidClientWrapper.instance = new PlaidClientWrapper();
}
return PlaidClientWrapper.instance.client;
}
}

View File

@@ -8,4 +8,5 @@ export const s3 = new S3Client({
secretAccessKey: config.s3.secretAccessKey,
},
endpoint: config.s3.endpoint,
forcePathStyle: config.s3.forcePathStyle,
});

View File

@@ -2,6 +2,7 @@ import moment from 'moment';
import * as R from 'ramda';
import { includes, isFunction, isObject, isUndefined, omit } from 'lodash';
import { formatNumber, sortObjectKeysAlphabetically } from 'utils';
import { EXPORT_DTE_FORMAT } from '@/services/Export/constants';
export class Transformer {
public context: any;
@@ -155,19 +156,35 @@ export class Transformer {
this.dateFormat = format;
}
/**
* Format date.
* @param {} date
* @param {string} format -
* @returns {}
*/
protected formatDate(date, format?: string) {
// Use the export date format if the async operation is in exporting,
// otherwise use the given or default format.
const _format = this.context.exportAls.isExport
? EXPORT_DTE_FORMAT
: format || this.dateFormat;
return date ? moment(date).format(_format) : '';
}
/**
*
* @param date
* @returns
* @returns {}
*/
protected formatDate(date) {
return date ? moment(date).format(this.dateFormat) : '';
protected formatDateFromNow(date) {
return date ? moment(date).fromNow(true) : '';
}
/**
*
* @param number
* @returns
* @returns {}
*/
protected formatNumber(number, props?) {
return formatNumber(number, { money: false, ...props });
@@ -177,7 +194,7 @@ export class Transformer {
*
* @param money
* @param options
* @returns
* @returns {}
*/
protected formatMoney(money, options?) {
return formatNumber(money, {

View File

@@ -3,12 +3,17 @@ import { isNull } from 'lodash';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import { TenantMetadata } from '@/system/models';
import { Transformer } from './Transformer';
import { ImportAls } from '@/services/Import/ImportALS';
import { ExportAls } from '@/services/Export/ExportAls';
@Service()
export class TransformerInjectable {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private exportAls: ExportAls;
/**
* Retrieves the application context of all tenant transformers.
* @param {number} tenantId
@@ -17,10 +22,12 @@ export class TransformerInjectable {
async getApplicationContext(tenantId: number) {
const i18n = this.tenancy.i18n(tenantId);
const organization = await TenantMetadata.query().findOne({ tenantId });
const exportAls = this.exportAls;
return {
organization,
i18n,
exportAls,
};
}

View File

@@ -1,7 +0,0 @@
import knexManager from 'knex-db-manager';
import { systemKnexConfig, systemDbManager } from 'config/knexConfig';
export default () => knexManager.databaseManagerFactory({
knex: systemKnexConfig,
dbManager: systemDbManager,
});

View File

@@ -3,7 +3,6 @@ import LoggerInstance from '@/loaders/logger';
import agendaFactory from '@/loaders/agenda';
import SmsClientLoader from '@/loaders/smsClient';
import mailInstance from '@/loaders/mail';
import dbManagerFactory from '@/loaders/dbManager';
import i18n from '@/loaders/i18n';
import repositoriesLoader from '@/loaders/systemRepositories';
import Cache from '@/services/Cache';
@@ -16,7 +15,6 @@ export default ({ mongoConnection, knex }) => {
try {
const agendaInstance = agendaFactory({ mongoConnection });
const smsClientInstance = SmsClientLoader(config.easySMSGateway.api_key);
const dbManager = dbManagerFactory(knex);
const cacheInstance = new Cache();
Container.set('logger', LoggerInstance);
@@ -24,7 +22,6 @@ export default ({ mongoConnection, knex }) => {
Container.set('SMSClient', smsClientInstance);
Container.set('mail', mailInstance);
Container.set('dbManager', dbManager);
LoggerInstance.info(
'[DI] Database manager has been injected into container.'
);

View File

@@ -113,7 +113,12 @@ import { UnlinkBankRuleOnDeleteBankRule } from '@/services/Banking/Rules/events/
import { DecrementUncategorizedTransactionOnMatching } from '@/services/Banking/Matching/events/DecrementUncategorizedTransactionsOnMatch';
import { DecrementUncategorizedTransactionOnExclude } from '@/services/Banking/Exclude/events/DecrementUncategorizedTransactionOnExclude';
import { DecrementUncategorizedTransactionOnCategorize } from '@/services/Cashflow/subscribers/DecrementUncategorizedTransactionOnCategorize';
import { DisconnectPlaidItemOnAccountDeleted } from '@/services/Banking/BankAccounts/events/DisconnectPlaidItemOnAccountDeleted';
import { LoopsEventsSubscriber } from '@/services/Loops/LoopsEventsSubscriber';
import { DeleteUncategorizedTransactionsOnAccountDeleting } from '@/services/Banking/BankAccounts/events/DeleteUncategorizedTransactionsOnAccountDeleting';
import { SeedInitialDemoAccountDataOnOrgBuild } from '@/services/OneClickDemo/events/SeedInitialDemoAccountData';
import { TriggerInvalidateCacheOnSubscriptionChange } from '@/services/Subscription/events/TriggerInvalidateCacheOnSubscriptionChange';
import { EventsTrackerListeners } from '@/services/EventsTracker/events/events';
export default () => {
return new EventPublisher();
@@ -244,8 +249,10 @@ export const susbcribers = () => {
DeleteCashflowTransactionOnUncategorize,
PreventDeleteTransactionOnDelete,
// Subscription
SubscribeFreeOnSignupCommunity,
SendVerfiyMailOnSignUp,
TriggerInvalidateCacheOnSubscriptionChange,
// Attachments
AttachmentsOnSaleInvoiceCreated,
@@ -275,8 +282,15 @@ export const susbcribers = () => {
// Plaid
RecognizeSyncedBankTranasctions,
DisconnectPlaidItemOnAccountDeleted,
DeleteUncategorizedTransactionsOnAccountDeleting,
// Loops
LoopsEventsSubscriber
LoopsEventsSubscriber,
// Demo Account
SeedInitialDemoAccountDataOnOrgBuild,
...EventsTrackerListeners
];
};

View File

@@ -34,4 +34,4 @@
// import 'services/Sales/SaleInvoiceWriteoffSubscriber';
// import 'subscribers/SaleInvoices/SendSmsNotificationToCustomer';
// import 'subscribers/SaleReceipt/SendNotificationToCustomer';
// import 'services/Sales/PaymentReceives/PaymentReceiveSmsSubscriber';
// import 'services/Sales/PaymentReceived/PaymentReceiveSmsSubscriber';

View File

@@ -2,6 +2,7 @@ import { I18n } from 'i18n';
export default () => new I18n({
locales: ['en', 'ar'],
defaultLocale: 'en',
register: global,
directory: global.__locales_dir,
updateFiles: false,

View File

@@ -9,11 +9,13 @@ import { SendSaleInvoiceMailJob } from '@/services/Sales/Invoices/SendSaleInvoic
import { SendSaleInvoiceReminderMailJob } from '@/services/Sales/Invoices/SendSaleInvoiceMailReminderJob';
import { SendSaleEstimateMailJob } from '@/services/Sales/Estimates/SendSaleEstimateMailJob';
import { SaleReceiptMailNotificationJob } from '@/services/Sales/Receipts/SaleReceiptMailNotificationJob';
import { PaymentReceiveMailNotificationJob } from '@/services/Sales/PaymentReceives/PaymentReceiveMailNotificationJob';
import { PaymentReceivedMailNotificationJob } from '@/services/Sales/PaymentReceived/PaymentReceivedMailNotificationJob';
import { PlaidFetchTransactionsJob } from '@/services/Banking/Plaid/PlaidFetchTransactionsJob';
import { ImportDeleteExpiredFilesJobs } from '@/services/Import/jobs/ImportDeleteExpiredFilesJob';
import { SendVerifyMailJob } from '@/services/Authentication/jobs/SendVerifyMailJob';
import { RegonizeTransactionsJob } from '@/services/Banking/RegonizeTranasctions/RecognizeTransactionsJob';
import { ReregonizeTransactionsJob } from '@/services/Banking/RegonizeTranasctions/jobs/RerecognizeTransactionsJob';
import { RegonizeTransactionsJob } from '@/services/Banking/RegonizeTranasctions/jobs/RecognizeTransactionsJob';
import { RevertRegonizeTransactionsJob } from '@/services/Banking/RegonizeTranasctions/jobs/RevertRecognizedTransactionsJob';
export default ({ agenda }: { agenda: Agenda }) => {
new ResetPasswordMailJob(agenda);
@@ -26,11 +28,13 @@ export default ({ agenda }: { agenda: Agenda }) => {
new SendSaleInvoiceReminderMailJob(agenda);
new SendSaleEstimateMailJob(agenda);
new SaleReceiptMailNotificationJob(agenda);
new PaymentReceiveMailNotificationJob(agenda);
new PaymentReceivedMailNotificationJob(agenda);
new PlaidFetchTransactionsJob(agenda);
new ImportDeleteExpiredFilesJobs(agenda);
new SendVerifyMailJob(agenda);
new RegonizeTransactionsJob(agenda);
new ReregonizeTransactionsJob(agenda);
new RevertRegonizeTransactionsJob(agenda);
agenda.start().then(() => {
agenda.every('1 hours', 'delete-expired-imported-files', {});

View File

@@ -197,6 +197,7 @@ export default class Account extends mixin(TenantModel, [
const ExpenseEntry = require('models/ExpenseCategory');
const ItemEntry = require('models/ItemEntry');
const UncategorizedTransaction = require('models/UncategorizedCashflowTransaction');
const PlaidItem = require('models/PlaidItem');
return {
/**
@@ -321,6 +322,18 @@ export default class Account extends mixin(TenantModel, [
query.where('categorized', false);
},
},
/**
* Account model may belongs to a Plaid item.
*/
plaidItem: {
relation: Model.BelongsToOneRelation,
modelClass: PlaidItem.default,
join: {
from: 'accounts.plaidItemId',
to: 'plaid_items.plaidItemId',
},
},
};
}

View File

@@ -1,3 +1,5 @@
import { Features } from '@/interfaces';
export default {
defaultFilterField: 'vendor',
defaultSort: {
@@ -167,6 +169,18 @@ export default {
},
},
},
branch: {
name: 'Branch',
type: 'text',
accessor: 'branch.name',
features: [Features.BRANCHES],
},
warehouse: {
name: 'Warehouse',
type: 'text',
accessor: 'warehouse.name',
features: [Features.BRANCHES],
},
},
fields2: {
billNumber: {
@@ -238,6 +252,22 @@ export default {
},
},
},
branchId: {
name: 'Branch',
fieldType: 'relation',
relationModel: 'Branch',
relationImportMatch: ['name', 'code'],
features: [Features.BRANCHES],
required: true,
},
warehouseId: {
name: 'Warehouse',
fieldType: 'relation',
relationModel: 'Warehouse',
relationImportMatch: ['name', 'code'],
features: [Features.WAREHOUSES],
required: true,
},
},
};

View File

@@ -402,6 +402,7 @@ export default class Bill extends mixin(TenantModel, [
const ItemEntry = require('models/ItemEntry');
const BillLandedCost = require('models/BillLandedCost');
const Branch = require('models/Branch');
const Warehouse = require('models/Warehouse');
const TaxRateTransaction = require('models/TaxRateTransaction');
const Document = require('models/Document');
const { MatchedBankTransaction } = require('models/MatchedBankTransaction');
@@ -453,6 +454,18 @@ export default class Bill extends mixin(TenantModel, [
},
},
/**
* Bill may has associated warehouse.
*/
warehouse: {
relation: Model.BelongsToOneRelation,
modelClass: Warehouse.default,
join: {
from: 'bills.warehouseId',
to: 'warehouses.id',
},
},
/**
* Bill may has associated tax rate transactions.
*/

View File

@@ -1,3 +1,5 @@
import { Features } from '@/interfaces';
export default {
defaultFilterField: 'vendor',
defaultSort: {
@@ -5,6 +7,8 @@ export default {
sortField: 'bill_date',
},
exportable: true,
exportFlattenOn: 'entries',
importable: true,
importAggregator: 'group',
importAggregateOn: 'entries',
@@ -77,7 +81,7 @@ export default {
paymentDate: {
name: 'bill_payment.field.payment_date',
type: 'date',
accessor: 'formattedPaymentDate'
accessor: 'formattedPaymentDate',
},
paymentNumber: {
name: 'bill_payment.field.payment_number',
@@ -111,6 +115,40 @@ export default {
name: 'bill_payment.field.reference',
type: 'text',
},
entries: {
name: 'Entries',
accessor: 'entries',
type: 'collection',
collectionOf: 'object',
columns: {
date: {
name: 'Bill date',
accessor: 'bill.formattedBillDate',
},
billNo: {
name: 'Bill No.',
accessor: 'bill.billNo',
},
billRefNo: {
name: 'Bill Reference No.',
accessor: 'bill.referenceNo',
},
billAmount: {
name: 'Bill Amount',
accessor: 'bill.totalFormatted',
},
paidAmount: {
name: 'Paid Amount',
accessor: 'paymentAmountFormatted',
},
},
},
branch: {
name: 'Branch',
type: 'text',
accessor: 'branch.name',
features: [Features.BRANCHES],
},
},
fields2: {
vendorId: {
@@ -174,5 +212,13 @@ export default {
},
},
},
branchId: {
name: 'Branch',
fieldType: 'relation',
relationModel: 'Branch',
relationImportMatch: ['name', 'code'],
features: [Features.BRANCHES],
required: true,
},
},
};

View File

@@ -3,7 +3,7 @@ import TenantModel from 'models/TenantModel';
import ModelSetting from './ModelSetting';
import BillPaymentSettings from './BillPayment.Settings';
import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from '@/services/Sales/PaymentReceives/constants';
import { DEFAULT_VIEWS } from '@/services/Sales/PaymentReceived/constants';
import ModelSearchable from './ModelSearchable';
export default class BillPayment extends mixin(TenantModel, [

View File

@@ -0,0 +1,11 @@
export default {
fields2: {
name: {
name: 'Name',
fieldType: 'text',
required: true,
},
},
columns: {},
fields: {}
};

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