Compare commits

...

433 Commits

Author SHA1 Message Date
Ahmed Bouhuolia
cf78255220 Merge pull request #700 from bigcapitalhq/fix-company-logo-dimenstion-pdf-template
fix: Set max width/height to company logo of pdf templates
2024-10-08 10:12:03 +02:00
Ahmed Bouhuolia
f9aa6abdd7 fix: Set max width/height to company logo of pdf templates 2024-10-08 10:11:40 +02:00
Ahmed Bouhuolia
0a5e40a0d8 Merge pull request #699 from bigcapitalhq/fix-remove-logo-pdf-template
fix: Delete company logo from the PDF template
2024-10-08 08:21:03 +02:00
Ahmed Bouhuolia
4aa445fe35 fix: Delete company logo from the pdf template 2024-10-08 08:20:35 +02:00
Ahmed Bouhuolia
1a1095c99b Merge pull request #698 from bigcapitalhq/fix-estimate-initial-value-template
fix: Estimate customize values
2024-10-07 17:21:35 +02:00
Ahmed Bouhuolia
d92b46aa7b fix: Estimate customize values 2024-10-07 17:20:45 +02:00
Ahmed Bouhuolia
682d40cbf8 Merge pull request #697 from bigcapitalhq/fix-pdf-branding-templates-request-data
fix: Pdf branding templates request data
2024-10-07 16:11:09 +02:00
Ahmed Bouhuolia
b62f3b3fa6 chore: remove commented line 2024-10-07 16:10:59 +02:00
Ahmed Bouhuolia
e76d3b15ce fix: Pdf branding template initial values 2024-10-07 16:08:25 +02:00
Ahmed Bouhuolia
9edfb83221 fix: Pdf branding templates request data 2024-10-07 16:03:56 +02:00
Ahmed Bouhuolia
bbdfe00c7a Merge pull request #696 from bigcapitalhq/fix-changing-pdf-template
fix: Changing the pdf template of the invoice
2024-10-07 09:51:17 +02:00
Ahmed Bouhuolia
e3942551cd fix: Changing the pdf template of the invoice 2024-10-07 09:50:46 +02:00
Ahmed Bouhuolia
a0c1a21983 Merge pull request #695 from bigcapitalhq/feat-change-document-title-of-payment-page
feat: Change the document title of the payment page
2024-10-06 22:21:53 +02:00
Ahmed Bouhuolia
3358ce58bc feat: Change the document title of the payment page 2024-10-06 22:21:12 +02:00
Ahmed Bouhuolia
3cd54653a8 Merge pull request #694 from bigcapitalhq/lerna-shared
feat: Add shared packages to Docker container
2024-10-06 17:26:39 +02:00
Ahmed Bouhuolia
6cad929738 feat: Lerna shared 2024-10-06 17:20:28 +02:00
Ahmed Bouhuolia
184648040c Merge pull request #693 from bigcapitalhq/fix-display-country-name
fix: Display country name
2024-10-06 13:19:17 +02:00
Ahmed Bouhuolia
df9d277e66 fix: Display country name 2024-10-06 13:18:31 +02:00
Ahmed Bouhuolia
75ec315de2 Merge pull request #689 from bigcapitalhq/download-payment-link-invoice-pdf
feat: Download invoice pdf of the payment link
2024-10-05 21:48:07 +02:00
Ahmed Bouhuolia
c89b2367e6 fix: Download invoice pdf of the payment link page 2024-10-05 21:46:48 +02:00
Ahmed Bouhuolia
bca5b3481c Merge pull request #691 from bigcapitalhq/pdf-templates-layout
fix: Pdf templates layout
2024-10-05 21:26:37 +02:00
Ahmed Bouhuolia
59996e7a40 feat: re-layout server-side pdf template 2024-10-05 21:24:07 +02:00
Ahmed Bouhuolia
af5726c48c fix: Pdf templates layout 2024-10-05 19:01:34 +02:00
Ahmed Bouhuolia
90f08c5d51 Merge pull request #690 from bigcapitalhq/fix-remove-empty-lines-from-address
fix: Remove empty lines from address formats
2024-10-05 16:09:03 +02:00
Ahmed Bouhuolia
a0a9f4a768 fix: Remove empty lines from address formats 2024-10-05 16:08:09 +02:00
Ahmed Bouhuolia
2649f1c326 feat: Download invoice pdf of the payment link 2024-10-05 13:56:25 +02:00
Ahmed Bouhuolia
c5ff1e4d4a Merge pull request #688 from bigcapitalhq/fix-pdf-template-addresses-controlling
fix: pdf template addresses controlling
2024-10-03 17:13:07 +02:00
Ahmed Bouhuolia
c74c8e896a fix: pdf template addresses controlling 2024-10-03 17:12:12 +02:00
Ahmed Bouhuolia
55fdc47ff0 Merge pull request #687 from bigcapitalhq/assign-default-pdf-template
feat: Assign default PDF template automatically
2024-10-03 17:02:17 +02:00
Ahmed Bouhuolia
126eb221d0 feat: invalidate invoice state once change default template 2024-10-03 17:01:35 +02:00
Ahmed Bouhuolia
3c7e22be43 feat: Assign default pdf template automatically 2024-10-03 16:36:44 +02:00
Ahmed Bouhuolia
b23112bc92 feat: Assign default PDF template automatically 2024-10-02 18:18:57 +02:00
Ahmed Bouhuolia
cbc60b3c73 Merge pull request #684 from bigcapitalhq/getting-uploaded-object-uri
fix: Getting uploaded object uri
2024-10-01 15:09:53 +02:00
Ahmed Bouhuolia
6caa1311fd fix: Getting uploaded object uri 2024-10-01 15:09:21 +02:00
Ahmed Bouhuolia
cd0bbd11c3 chore: change CHANGELOG.md 2024-10-01 12:56:33 +02:00
Ahmed Bouhuolia
2a944f8507 feat: Add Stripe payment env variables examples 2024-10-01 12:53:27 +02:00
Ahmed Bouhuolia
8a2754d9ce Merge pull request #683 from bigcapitalhq/feat-hook-up-customer-address
feat: Hook up customer/company address to invoice preview of payment page
2024-10-01 09:49:14 +02:00
Ahmed Bouhuolia
ace75f2dfa feat: Hook up customer/company address to invoice preview of payment page 2024-10-01 09:48:07 +02:00
Ahmed Bouhuolia
7ceb785c1b Merge pull request #682 from bigcapitalhq/listen-stripe-integration-events
feat: Listen to Stripe integration events
2024-09-30 23:13:14 +02:00
Ahmed Bouhuolia
904a52f5a1 feat: listen to Stripe integration events 2024-09-30 23:12:42 +02:00
Ahmed Bouhuolia
04fe65b176 fix: payment link events tracker 2024-09-30 18:09:57 +02:00
Ahmed Bouhuolia
7ac6e0d349 Merge pull request #681 from bigcapitalhq/fix-pdf-template-customize-content
fix: Branding customize content
2024-09-30 14:52:36 +02:00
Ahmed Bouhuolia
4ec3586173 fix: branding customize content 2024-09-30 14:51:03 +02:00
Ahmed Bouhuolia
4b6ab7035e Merge pull request #680 from bigcapitalhq/add-posthog-events-tracking-to-pdf-templates
feat: Track pdf templates Posthog events
2024-09-30 12:44:27 +02:00
Ahmed Bouhuolia
3fe7babe00 feat: Track pdf templates Posthog events 2024-09-30 12:43:51 +02:00
Ahmed Bouhuolia
f21570982e Merge pull request #679 from bigcapitalhq/fix-listen-to-stripe-session-completed
fix: Listen to Stripe session completed event
2024-09-30 11:49:48 +02:00
Ahmed Bouhuolia
ad8fe52b84 fix: Listen to Stripe session completed event 2024-09-30 11:49:19 +02:00
Ahmed Bouhuolia
15ce6ac710 Merge pull request #678 from bigcapitalhq/pdf-templates-company-customer-address
feat: Pdf templates customer/company addresses
2024-09-30 11:21:14 +02:00
Ahmed Bouhuolia
783387dce6 fix: pdf templates server-side rendered 2024-09-30 11:15:05 +02:00
Ahmed Bouhuolia
863c7ad99f feat: Hook up customer/company address to pdf templates 2024-09-29 22:59:14 +02:00
Ahmed Bouhuolia
776b69475c feat: PDF templates company/customer address 2024-09-29 19:31:00 +02:00
Ahmed Bouhuolia
6b6027a588 feat: Pdf templates customer/company addresses 2024-09-29 18:04:56 +02:00
Ahmed Bouhuolia
d465ee15bd Merge pull request #677 from bigcapitalhq/preferences-company-branding
feat: Company branding preferences
2024-09-29 13:44:29 +02:00
Ahmed Bouhuolia
be2049ca6e feat: Pdf template address 2024-09-29 13:43:09 +02:00
Ahmed Bouhuolia
9b63c176cd feat: Hook up company address to payment page 2024-09-28 20:19:05 +02:00
Ahmed Bouhuolia
e506a7ba35 feat: Hook orgnization name and logo to payment page 2024-09-28 19:20:01 +02:00
Ahmed Bouhuolia
2191ad0d40 feat: hook up preferences branding form 2024-09-28 18:44:08 +02:00
Ahmed Bouhuolia
ca162206a3 feat: Organization address and branding patch endpoint 2024-09-28 17:43:47 +02:00
Ahmed Bouhuolia
c5d7a2bfd8 feat: Company branding preferences 2024-09-28 14:47:59 +02:00
Ahmed Bouhuolia
b9506424d1 Merge pull request #675 from bigcapitalhq/hook-up-company-logo-to-pdf-templates
feat: Hook up company logo to server-side pdf templates
2024-09-26 18:33:45 +02:00
Ahmed Bouhuolia
46a145ae58 feat: Hook up company logo to server-side pdf templates 2024-09-26 18:33:21 +02:00
Ahmed Bouhuolia
e4044ef563 Merge pull request #674 from bigcapitalhq/clean-up-payment-links-endpoints
feat: Clean up payment links endpoints
2024-09-25 19:38:28 +02:00
Ahmed Bouhuolia
1cc71eb368 feat: Clean up payment links endpoints 2024-09-25 19:37:40 +02:00
Ahmed Bouhuolia
323b95de7b Merge pull request #673 from bigcapitalhq/fix-invoice-customize-bugs
fix: Invoice customize bugs
2024-09-25 15:21:12 +02:00
Ahmed Bouhuolia
b0658be041 fix: Invoice customize bugs 2024-09-25 15:20:24 +02:00
Ahmed Bouhuolia
b222d56148 Merge pull request #672 from bigcapitalhq/fix-invoice-brand-customize
fix: Invoice pdf customize
2024-09-25 12:22:07 +02:00
Ahmed Bouhuolia
946872204b fix: payment page 2024-09-25 12:21:26 +02:00
Ahmed Bouhuolia
2f9adfd908 fix: Invoice pdf customize 2024-09-25 11:04:17 +02:00
Ahmed Bouhuolia
1c8e19378f Merge pull request #670 from bigcapitalhq/upload-company-logo
feat: Upload company logo to invoice templates
2024-09-24 20:31:12 +02:00
Ahmed Bouhuolia
37fd4a1fdb feat: Uploading company logo 2024-09-24 20:28:19 +02:00
Ahmed Bouhuolia
7aed3d9c8c Merge pull request #668 from bigcapitalhq/stripe-integrate
feat: Onboard accounts to Stripe Connect
2024-09-24 14:12:39 +02:00
Ahmed Bouhuolia
b125e3e58b feat: Stripe connect using OAuth 2024-09-24 14:10:53 +02:00
Ahmed Bouhuolia
70bba4a6ed fix: Stripe integration content 2024-09-23 17:34:27 +02:00
Ahmed Bouhuolia
1570995021 feat: Add Stripe pre-setup dialog 2024-09-23 14:44:07 +02:00
Ahmed Bouhuolia
8109236e72 fix: Stripe payment integration 2024-09-23 13:21:54 +02:00
Ahmed Bouhuolia
9ba651decb feat: Delete Stripe pamyent connection 2024-09-22 21:57:46 +02:00
Ahmed Bouhuolia
eb5fdbf4ee feat: Control the payment method from invoice form 2024-09-22 21:23:02 +02:00
Ahmed Bouhuolia
9827a84857 feat: Hook up edit Stripe settings form 2024-09-22 17:25:27 +02:00
Ahmed Bouhuolia
3308133736 feat: Edit Stripe payment settings 2024-09-22 14:55:48 +02:00
Ahmed Bouhuolia
3129c76c30 feat: Delete Stripe payment method 2024-09-22 14:30:47 +02:00
Ahmed Bouhuolia
c0a4c965f0 feat: Edit stripe payment integation drawer 2024-09-22 12:52:59 +02:00
Ahmed Bouhuolia
d16c57b63b feat: Upload company logo to invoice templates 2024-09-22 00:01:12 +02:00
Ahmed Bouhuolia
e04f5d26a3 feat: listen to stripe account updated webhook 2024-09-21 23:59:54 +02:00
Ahmed Bouhuolia
ad74007d58 fix: Style of paper template address 2024-09-21 20:04:23 +02:00
Ahmed Bouhuolia
6271d6c268 chore: remove newrelic logs file 2024-09-21 19:13:20 +02:00
Ahmed Bouhuolia
7756b5b304 feat: Stripe payment integration 2024-09-21 16:50:22 +02:00
Ahmed Bouhuolia
8de8695b25 feat: clean up the style of public payment page. 2024-09-21 09:53:00 +02:00
Ahmed Bouhuolia
11c56c75a4 feat: clean up the stripe payment integration 2024-09-21 09:18:39 +02:00
Ahmed Bouhuolia
f5a1d68c52 feat: Stripe payment checkout session 2024-09-19 22:24:07 +02:00
Ahmed Bouhuolia
0ae7a25c27 feat: Map the invoice preview data 2024-09-19 14:32:14 +02:00
Ahmed Bouhuolia
16eaacd4bc feat: Payment invoice preview drawer 2024-09-19 12:45:06 +02:00
Ahmed Bouhuolia
809973730f feat: style tweaks in the public payment page 2024-09-19 11:28:40 +02:00
Ahmed Bouhuolia
2ebb4595a8 feat: Emit Stripe webhooks to events in the system 2024-09-19 10:25:13 +02:00
Ahmed Bouhuolia
77f628509c fix: make the base url of payment link configurable 2024-09-18 23:53:46 +02:00
Ahmed Bouhuolia
d2cd32a735 feat: inactive associated Stripe payment link on invoice deleting 2024-09-18 23:41:59 +02:00
Ahmed Bouhuolia
4665f529e6 feat: integrate Stripe payment to invoices 2024-09-18 19:24:01 +02:00
Ahmed Bouhuolia
df706d2573 feat: payment methods preferences page 2024-09-18 11:19:59 +02:00
Ahmed Bouhuolia
5270e99de8 feat: select payment methods dialog 2024-09-18 10:43:21 +02:00
Ahmed Bouhuolia
eb48f66f6e Merge branch 'develop' into stripe-integrate 2024-09-17 19:26:13 +02:00
Ahmed Bouhuolia
5e7cff0eb7 Merge pull request #667 from bigcapitalhq/invoice-customize
feat: customize pdf templates
2024-09-17 19:21:26 +02:00
Ahmed Bouhuolia
34e781b4a2 fix: typo in invoice customize drawer 2024-09-17 19:18:22 +02:00
Ahmed Bouhuolia
5f40d50852 fix: pdf template customization 2024-09-17 18:19:28 +02:00
Ahmed Bouhuolia
bb0d91a9cb fix: pdf templates 2024-09-17 17:46:56 +02:00
Ahmed Bouhuolia
2c790427fa feat: rendering pdf templates on the server-side 2024-09-17 13:53:57 +02:00
Ahmed Bouhuolia
4f59b27d70 feat: hook up branding templates to invoices 2024-09-16 20:02:17 +02:00
Ahmed Bouhuolia
94c08f0b9e chore: clean pdf templates code 2024-09-15 22:55:39 +02:00
Ahmed Bouhuolia
ef4beaa564 feat: seed initial standard branding templates 2024-09-15 22:01:11 +02:00
Ahmed Bouhuolia
2b42215381 feat: add loading state to generate payment link dialog 2024-09-15 21:08:41 +02:00
Ahmed Bouhuolia
18d6ec7b59 feat: style the generate payment link dialog 2024-09-15 21:03:36 +02:00
Ahmed Bouhuolia
430cf19533 feat: Link transations with payment methods 2024-09-15 19:42:43 +02:00
Ahmed Bouhuolia
542e61dbfc feat: sharable payment link dialog 2024-09-15 19:28:43 +02:00
Ahmed Bouhuolia
8566422ce3 fix: Add mising address in branding templates customize 2024-09-14 22:52:37 +02:00
Ahmed Bouhuolia
9517b4e279 feat: wip public payment page 2024-09-14 22:10:27 +02:00
Ahmed Bouhuolia
70551bee30 feat: the element customize submit button 2024-09-14 20:18:03 +02:00
Ahmed Bouhuolia
d690c6a3fe feat: optimize branding templates customiing 2024-09-14 19:32:16 +02:00
Ahmed Bouhuolia
28319c2cdc feat: cannot delete a predefined branding template 2024-09-14 16:26:34 +02:00
Ahmed Bouhuolia
df0f73f338 feat: mark specific template as default 2024-09-14 16:19:06 +02:00
Ahmed Bouhuolia
411ac55986 feat: templates customize 2024-09-12 17:49:00 +02:00
Ahmed Bouhuolia
12226d469a feat: pdf template customize 2024-09-12 16:50:44 +02:00
Ahmed Bouhuolia
632c4629de feat: hook up the invice customize api 2024-09-12 14:16:07 +02:00
Ahmed Bouhuolia
a7df23cebc feat: branding templates table 2024-09-11 21:16:21 +02:00
Ahmed Bouhuolia
ef74e250f1 feat: link pdf template to sales transactions 2024-09-11 16:49:44 +02:00
Ahmed Bouhuolia
c0769662bd feat(server): add pdf template crud endpoints 2024-09-11 14:54:13 +02:00
Ahmed Bouhuolia
5b6270a184 feat: invoice pdf customize 2024-09-10 23:32:34 +02:00
Ahmed Bouhuolia
4541d28b68 chore: dump CHANGELOG 2024-09-10 22:25:16 +02:00
Ahmed Bouhuolia
716dec799a feat: paper template customize 2024-09-10 21:54:37 +02:00
Ahmed Bouhuolia
77a1e35ff4 feat: Paper template reusable 2024-09-10 18:37:38 +02:00
Ahmed Bouhuolia
317adfa0de feat: wip estimate, receipt, payment received customize 2024-09-10 17:06:17 +02:00
Ahmed Bouhuolia
f0dfc3d1b0 feat: invoice customize paper preview 2024-09-10 13:29:25 +02:00
Ahmed Bouhuolia
67904f52af feat: add more customize drawers 2024-09-09 21:31:14 +02:00
Ahmed Bouhuolia
f644ed6708 feat: element customize component 2024-09-09 21:07:22 +02:00
Ahmed Bouhuolia
dc18bde6be feat: wip invoice customizer 2024-09-09 19:40:23 +02:00
Ahmed Bouhuolia
132c1dfdbe feat: craft the paper template style 2024-09-09 16:24:09 +02:00
Ahmed Bouhuolia
162b92ce84 feat: wip Stripe connect integration 2024-09-09 14:18:04 +02:00
Ahmed Bouhuolia
9247745ab0 feat: wip invoice customize 2024-09-08 21:01:54 +02:00
Ahmed Bouhuolia
c5c0342c7b feat: wip styling invoice customize 2024-09-08 20:13:27 +02:00
Ahmed Bouhuolia
f5e9485a12 feat: wip invoice customize 2024-09-08 17:34:19 +02:00
Ahmed Bouhuolia
a183666df6 feat: Onboard accounts to Stripe Connect 2024-09-08 11:42:26 +02:00
Ahmed Bouhuolia
e6bad27771 feat: wip invoice customizer 2024-09-07 21:39:05 +02:00
Ahmed Bouhuolia
6d24474162 Merge pull request #663 from bigcapitalhq/fix-uncategorize-bank-transaction
fix: Un-categorize bank transactions
2024-09-07 13:27:11 +02:00
Ahmed Bouhuolia
5962b990c4 fix: Uncategorize bank transactions 2024-09-07 13:26:02 +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
Ahmed Bouhuolia
ded4e2bb59 Merge pull request #560 from bigcapitalhq/fix-onboarding-on-small-screens
fix: Onboarding layout on small screens
2024-08-01 19:53:09 +02:00
Ahmed Bouhuolia
219e6fb466 fix: onboarding page layout on small screens 2024-08-01 19:51:25 +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
4cd0405078 fix: quick payment received and payment made form initial values 2024-07-30 11:06:45 +02:00
Ahmed Bouhuolia
783102449f fix: create quick payment received and payment made transactions 2024-07-30 11:06:35 +02:00
Denis
ae617b2e1d Fixed Quick Payment Dialogs
PaymentReceives and BillsPayments Controllers expect 'amount' parameter, but webapp sends 'payment_amount'
2024-07-30 11:06:24 +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
53f37f4f48 Merge pull request #546 from bigcapitalhq/remove-views-tabs
feat: Remove the views tabs bar from all tables
2024-07-25 19:21:50 +02:00
Ahmed Bouhuolia
0a7b522b87 chore: remove unused import 2024-07-25 19:21:16 +02:00
Ahmed Bouhuolia
9e6500ac79 feat: remove the views tabs bar from all tables 2024-07-25 19:17:54 +02:00
Ahmed Bouhuolia
b93cb546f4 Merge pull request #545 from bigcapitalhq/excessed-payments-as-credit
Excessed payments as credit
2024-07-25 18:57:31 +02:00
Ahmed Bouhuolia
6d17f9cbeb feat: record excessed payments as credit 2024-07-25 18:46:24 +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
fe214b1b2d feat: push CHANGELOG 2024-07-17 16:53:47 +02:00
Ahmed Bouhuolia
6b6b73b77c feat: send signup event to Loops (#531)
* feat: send signup event to Loops

* feat: fix
2024-07-17 15:56:05 +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
Ahmed Bouhuolia
107a6f793b Merge pull request #526 from bigcapitalhq/monthly-plans
feat: upgrade the subscription plans
2024-07-14 14:21:57 +02:00
Ahmed Bouhuolia
67d155759e feat: backend the new monthly susbcription plans 2024-07-14 14:19:04 +02:00
Ahmed Bouhuolia
7e2e87256f Merge pull request #527 from bigcapitalhq/fix-sync-removed-transactions
fix: sync the removed bank transactions from the source
2024-07-13 21:56:13 +02:00
Ahmed Bouhuolia
df7790d7c1 fix: sync the removed bank transactions from the source 2024-07-13 21:54:44 +02:00
Ahmed Bouhuolia
72128a72c4 feat: add variant ids to new subscription plans 2024-07-13 19:53:52 +02:00
Ahmed Bouhuolia
eb3f23554f feat: upgrade the subscription plans 2024-07-13 18:19:18 +02:00
Ahmed Bouhuolia
69ddf43b3e fix: duplicated event emitter 2024-07-13 03:23:25 +02:00
Ahmed Bouhuolia
249eadaeaa Merge pull request #525 from bigcapitalhq/fix-plaid-transactions-syncing
fix: Plaid transactions syncing
2024-07-12 23:44:27 +02:00
1077 changed files with 33875 additions and 5183 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,14 @@ S3_ACCESS_KEY_ID=
S3_SECRET_ACCESS_KEY=
S3_ENDPOINT=
S3_BUCKET=
# PostHog
POSTHOG_API_KEY=
POSTHOG_HOST=
# Stripe Payment
STRIPE_PAYMENT_SECRET_KEY=
STRIPE_PAYMENT_PUBLISHABLE_KEY=
STRIPE_PAYMENT_CLIENT_ID=
STRIPE_PAYMENT_WEBHOOKS_SECRET=
STRIPE_PAYMENT_REDIRECT_URL=

View File

@@ -2,6 +2,131 @@
All notable changes to Bigcapital server-side will be in this file.
# [0.20.0]
* feat: Customize pdf templates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/667
* feat: Onboard accounts to Stripe Connect by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/668
* feat: Upload company logo to invoice templates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/670
* fix: Invoice pdf customize by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/672
* fix: Invoice customize bugs by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/673
* feat: Clean up payment links endpoints by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/674
* feat: Hook up company logo to server-side pdf templates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/675
* feat: Company branding preferences by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/677
* feat: Pdf templates customer/company addresses by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/678
* fix: Listen to Stripe session completed event by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/679
* feat: Track pdf templates Posthog events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/680
* fix: Branding customize content by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/681
* feat: Listen to Stripe integration events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/682
* feat: Hook up customer/company address to invoice preview of payment page by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/683
# [0.19.17]
* fix: Un-categorize bank transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/663
# [0.19.16]
* feat: Tracking more Posthog events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/653
* fix: Expense cannot accept credit card as payment account by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/654
* fix: Suspense the lazy loaded components in banking pages by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/657
* feat: Add help dropdown menu by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/656
* feat: Bank pages layout breaking by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/658
* feat: Datatable UI improvements by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/655
* fix: Array cast of recognize function rule ids by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/660
* fix: Payment made filling the form full amount field by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/661
* feat: Tabular number of all money columns by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/659
* refactor: The expense G/L writer by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/662
## [0.19.15] -
* fix: Bank transactions infinity scrolling by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/648
* feat: Integrate multiple branches and warehouses to resource importing by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/645
* fix: Integrate multiple branches with expense resource by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/649
* feat: Cover more tracking events. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/650
* feat: Track banking service events by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/651
## [0.19.14]
* fix: Import bugs by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/643
* fix: Set default index to transaction entries by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/644
* feat(server): Events tracking using Posthog by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/646
## [0.19.13]
* fix: Subscription middleware by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/624
* fix: Getting the sheet columns in import sheet by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/641
## [0.19.12]
* fix: Typo one-click demo page by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/640
## [0.19.11]
* fix: Avoid running the cost job in import preview by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/635
* fix: Debounce scheduling calculating items cost by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/634
* fix: Expand the resources export page size limitation by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/636
* feat: Optimize loading perf. by splitting big chunks and lazy loading them by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/632
* fix: Use standard ISO 8601 format for exported data by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/638
* fix: Add customer type to customers resource by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/639
## [0.19.10]
* fix: Add subscription plans offer text by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/629
## [0.19.9]
* fix: Make webapp package env variables dynamic by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/628
## [v0.19.8]
* fix: Cannot import items income and cost accounts by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/617
* fix: Some bank account details hidden by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/618
* feat(ee): One-click demo account by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/616
* feat: change banking service language by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/619
* feat(banking): Filter uncategorized bank transactions by date by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/590
* Fix: Syntax error caused error by @wolone in https://github.com/bigcapitalhq/bigcapital/pull/622
* fix: Listen to payment webhooks by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/623
* fix: Add prefix J-00001 to manual journals increments by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/625
* fix: Disable sms service until Twilo integration by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/626
* fix: Style tweaks in onboarding page by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/627
## [0.19.5] - 18-08-2024
* fix: Allow multi-lines to statements transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/594
* 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.19.4]
* feat: Import and export tax rates by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/591
* feat: Un-categorize bank transactions in bulk by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/587
* feat: Pending bank transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/589
* fix: Update `dev` Script in `package.json` to Use `cross-env` by @Champetaman in https://github.com/bigcapitalhq/bigcapital/pull/588
* fix: Should not load branches on reconcile matching form if the branches not enabled by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/592
* fix: Rounding the total amount the pending and matched transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/593
## [v0.18.0] - 10-08-2024
* feat: Bank rules for automated categorization by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/511
* feat: Categorize & match bank transaction by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/511
* feat: Reconcile match transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/522
* fix: Issues in matching transactions by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/523
* fix: Cashflow transactions types by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/524
## [v0.17.5] - 17-06-2024
* fix: Balance sheet and P/L nested accounts by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/501

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

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

View File

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

View File

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

View File

@@ -22,6 +22,7 @@
"dependencies": {
"@aws-sdk/client-s3": "^3.576.0",
"@aws-sdk/s3-request-presigner": "^3.583.0",
"@bigcapital/utils": "*",
"@casl/ability": "^5.4.3",
"@hapi/boom": "^7.4.3",
"@lemonsqueezy/lemonsqueezy.js": "^2.2.0",
@@ -37,6 +38,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 +71,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 +100,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,11 +110,13 @@
"rtl-detect": "^1.0.4",
"socket.io": "^4.7.4",
"source-map-loader": "^4.0.1",
"stripe": "^16.10.0",
"tmp-promise": "^3.0.3",
"ts-transformer-keys": "^0.4.2",
"tsyringe": "^4.3.0",
"typedi": "^0.8.0",
"uniqid": "^5.2.0",
"uuid": "^10.0.0",
"winston": "^3.2.1",
"xlsx": "^0.18.5",
"yup": "^0.28.1"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,82 +1,227 @@
extends ../PaperTemplateLayout.pug
block head
style
if (isRtl)
include ../../css/modules/estimate-rtl.css
else
include ../../css/modules/estimate.css
block head
- var prefix = 'bc'
style.
.#{prefix}-root {
color: #111;
padding: 24px 30px;
font-size: 12px;
position: relative;
box-shadow: inset 0 4px 0px 0 var(--invoice-primary-color);
}
.#{prefix}-header {
box-sizing: border-box;
display: flex;
flex-flow: wrap;
flex: 0 0 auto;
-webkit-box-align: start;
align-items: start;
-webkit-box-pack: start;
justify-content: flex-start;
gap: 10px;
}
.#{prefix}-header-details {
flex: 1;
display: flex;
flex-direction: column;
align-items: stretch;
gap: 20px;
flex: 1 1 0%;
}
.#{prefix}-big-title {
font-size: 30px;
margin: 0;
line-height: 1;
font-weight: 500;
color: #333;
}
.#{prefix}-logo-wrap img {
width: 100%;
height: 100%;
max-width: 260px;
max-height: 100px;
}
.#{prefix}-terms {
display: flex;
flex-direction: column;
gap: 4px;
margin-bottom: 24px;
}
.#{prefix}-terms-item {
display: flex;
flex-direction: row;
gap: 12px;
}
.#{prefix}-terms-item__label {
min-width: 120px;
color: #333;
}
.#{prefix}-terms-item__value {
}
.#{prefix}-addresses{
box-sizing: border-box;
display: flex;
flex-flow: wrap;
align-items: flex-start;
justify-content: flex-start;
gap: 10px;
margin-bottom: 24px;
}
.#{prefix}-addresses > * {
flex: 1 1;
}
.#{prefix}-address {
}
.#{prefix}-address__item {
}
.#{prefix}-table {
width: 100%;
border-collapse: collapse;
text-align: left;
font-size: inherit;
}
.#{prefix}-table__header {
font-weight: 400;
border-bottom: 1px solid #000;
padding: 2px 10px;
color: #333;
}
.#{prefix}-table__header:first-of-type{
padding-left: 0;
}
.#{prefix}-table__header:last-of-type{
padding-right: 0;
}
.#{prefix}-table__header--right {
text-align: right;
}
.#{prefix}-table__cell {
border-bottom: 1px solid #F6F6F6;
padding: 12px 10px;
}
.#{prefix}-table__cell--right{
text-align: right;
}
.#{prefix}-table__cell:first-of-type{
padding-left: 0;
}
.#{prefix}-table__cell:last-of-type {
padding-right: 0;
}
.#{prefix}-totals {
display: flex;
flex-direction: column;
margin-left: auto;
width: 300px;
margin-bottom: 24px;
}
.#{prefix}-totals__item {
display: flex;
padding: 4px 0;
}
.#{prefix}-totals__item--border-gray {
border-bottom: 1px solid #DADADA;
}
.#{prefix}-totals__item--border-dark {
border-bottom: 1px solid #000;
}
.#{prefix}-totals__item--font-weight-bold {
font-weight: bold;
}
.#{prefix}-totals__item-label {
min-width: 160px;
}
.#{prefix}-totals__item-amount {
flex: 1 1 auto;
text-align: right;
}
.#{prefix}-statement {
margin-bottom: 20px;
}
.#{prefix}-statement__label {
color: #666;
}
.#{prefix}-statement__value {
}
block content
div.estimate
div.estimate__header
div.paper
h1.title #{__("estimate.paper.estimate")}
span.email #{saleEstimate.estimateNumber}
div(class=`${prefix}-root`, style=`--invoice-primary-color: ${primaryColor}; --invoice-secondary-color: ${secondaryColor};`)
div.organization
h3.title #{organizationName}
if organizationEmail
span.email #{organizationEmail}
//- Header (invluces big title, details and logo)
div(class=`${prefix}-header`)
div.estimate__estimate-amount
div.label #{__('estimate.paper.estimate_amount')}
div.amount #{saleEstimate.formattedAmount}
//- Header details (includes big title and details )
div(class=`${prefix}-header-details`)
h1(class=`${prefix}-big-title`) Estimate
div.estimate__meta
if saleEstimate.estimateNumber
div.estimate__meta-item.estimate__meta-item--estimate-number
span.label #{__("estimate.paper.estimate_number")}
span.value #{saleEstimate.estimateNumber}
//- Terms List
div(class=`${prefix}-terms`)
if showEstimateNumber
div(class=`${prefix}-terms-item`)
div(class=`${prefix}-terms-item__label`) #{estimateNumberLabel}
div(class=`${prefix}-terms-item__value`) #{estimateNumebr}
div.estimate__meta-item.estimate__meta-item--billed-to
span.label #{__("estimate.paper.billed_to")}
span.value #{saleEstimate.customer.displayName}
if showEstimateDate
div(class=`${prefix}-terms-item`)
div(class=`${prefix}-terms-item__label`) #{estimateDateLabel}
div(class=`${prefix}-terms-item__value`) #{estimateDate}
div.estimate__meta-item.estimate__meta-item--estimate-date
span.label #{__("estimate.paper.estimate_date")}
span.value #{saleEstimate.formattedEstimateDate}
if showExpirationDate
div(class=`${prefix}-terms-item`)
div(class=`${prefix}-terms-item__label`) #{expirationDateLabel}
div(class=`${prefix}-terms-item__value`) #{expirationDate}
div.estimate__meta-item.estimate__meta-item--due-date
span.label #{__("estimate.paper.expiration_date")}
span.value #{saleEstimate.formattedExpirationDate}
//- Company logo
if showCompanyLogo && companyLogoUri
div(class=`${prefix}-logo-wrap`)
img(alt="Company logo", src=companyLogoUri)
div.estimate__table
table
thead
tr
th.item #{__("item_entry.paper.item_name")}
th.rate #{__("item_entry.paper.rate")}
th.quantity #{__("item_entry.paper.quantity")}
th.total #{__("item_entry.paper.total")}
//- Addresses (Group section)
div(class=`${prefix}-addresses`)
if showCompanyAddress
div(class=`${prefix}-address-from`)
div !{companyAddress}
if showCustomerAddress
div(class=`${prefix}-address-to`)
strong #{billedToLabel}
div !{customerAddress}
//- Table section (Line items)
table(class=`${prefix}-table`)
thead
tr
th(class=`${prefix}-table__header`) Item
th(class=`${prefix}-table__header`) Description
th(class=`${prefix}-table__header ${prefix}-table__header--right`) Rate
th(class=`${prefix}-table__header ${prefix}-table__header--right`) Total
tbody
each entry in saleEstimate.entries
tr
td.item
div.title=entry.item.name
span.description=entry.description
td.rate=entry.rate
td.quantity=entry.quantity
td.total=entry.amount
each line in lines
tr
td(class=`${prefix}-table__cell`) #{line.item}
td(class=`${prefix}-table__cell`) #{line.description}
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.rate}
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.total}
div.estimate__table-after
div.estimate__table-total
table
tbody
tr.subtotal
td #{__('estimate.paper.subtotal')}
td #{saleEstimate.formattedAmount}
tr.total
td #{__('estimate.paper.total')}
td #{saleEstimate.formattedAmount}
//- Totals section
div(class=`${prefix}-totals`)
if showSubtotal
div(class=`${prefix}-totals__item ${prefix}-totals__item--border-gray`)
div(class=`${prefix}-totals__item-label`) #{subtotalLabel}
div(class=`${prefix}-totals__item-amount`) #{subtotal}
if showTotal
div(class=`${prefix}-totals__item ${prefix}-totals__item--border-dark ${prefix}-totals__item--font-weight-bold`)
div(class=`${prefix}-totals__item-label`) #{totalLabel}
div(class=`${prefix}-totals__item-amount`) #{total}
div.estimate__footer
if saleEstimate.termsConditions
div.estimate__conditions
h3 #{__("estimate.paper.conditions_title")}
p #{saleEstimate.termsConditions}
//- Statements section
if showCustomerNote && customerNote
div(class=`${prefix}-statement`)
div(class=`${prefix}-statement__label`) #{customerNoteLabel}
div(class=`${prefix}-statement__value`) #{customerNote}
if saleEstimate.note
div.estimate__notes
h3 #{__("estimate.paper.notes_title")}
p #{saleEstimate.note}
if showTermsConditions && termsConditions
div(class=`${prefix}-statement`)
div(class=`${prefix}-statement__label`) #{termsConditionsLabel}
div(class=`${prefix}-statement__value`) #{termsConditions}

View File

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

View File

@@ -0,0 +1,258 @@
extends ../PaperTemplateLayout.pug
block head
- var prefix = 'bc'
style.
.#{prefix}-root {
color: #111;
padding: 24px 30px;
font-size: 12px;
position: relative;
box-shadow: inset 0 4px 0px 0 var(--invoice-primary-color);
}
.#{prefix}-header{
box-sizing: border-box;
display: flex;
flex-flow: wrap;
flex: 0 0 auto;
-webkit-box-align: start;
align-items: start;
-webkit-box-pack: start;
justify-content: flex-start;
gap: 10px;
}
.#{prefix}-header-details{
flex: 1;
display: flex;
flex-direction: column;
align-items: stretch;
gap: 20px;
flex: 1 1 0%;
}
.#{prefix}-big-title {
font-size: 30px;
margin: 0;
line-height: 1;
font-weight: 500;
color: #333;
}
.#{prefix}-logo-wrap img {
width: 100%;
height: 100%;
max-width: 260px;
max-height: 100px;
}
.#{prefix}-details {
display: flex;
flex-direction: column;
gap: 4px;
margin-bottom: 24px;
}
.#{prefix}-detail {
display: flex;
flex-direction: row;
gap: 12px;
}
.#{prefix}-detail__label {
min-width: 120px;
color: #333;
}
.#{prefix}-detail__value {
/* Styles for detail values */
}
.#{prefix}-address-root {
box-sizing: border-box;
display: flex;
flex-flow: wrap;
align-items: flex-start;
justify-content: flex-start;
gap: 10px;
margin-bottom: 24px;
}
.#{prefix}-address-from {
flex: 1;
}
.#{prefix}-address-from__item {
/* Styles for items in the billed-from address */
}
.#{prefix}-address-to {
flex: 1;
}
.#{prefix}-address-to__item {
/* Styles for items in the billed-to address */
}
.#{prefix}-table {
width: 100%;
border-collapse: collapse;
text-align: left;
font-size: inherit;
}
.#{prefix}-table__header {
font-weight: 400;
border-bottom: 1px solid #000;
padding: 2px 10px;
color: #333;
}
.#{prefix}-table__header:first-of-type{
padding-left: 0;
}
.#{prefix}-table__header:last-of-type{
padding-right: 0;
}
.#{prefix}-table__header--right {
text-align: right;
}
.#{prefix}-table__cell {
border-bottom: 1px solid #F6F6F6;
padding: 12px 10px;
}
.#{prefix}-table__cell:first-of-type{
padding-left: 0;
}
.#{prefix}-table__cell:last-of-type {
padding-right: 0;
}
.#{prefix}-table__cell--right {
text-align: right;
}
.#{prefix}-totals {
display: flex;
flex-direction: column;
margin-left: auto;
width: 300px;
margin-bottom: 24px;
}
.#{prefix}-totals__item {
display: flex;
padding: 4px 0;
}
.#{prefix}-totals__item--border-gray {
border-bottom: 1px solid #DADADA;
}
.#{prefix}-totals__item--border-dark {
border-bottom: 1px solid #000;
}
.#{prefix}-totals__item--font-weight-bold {
font-weight: bold;
}
.#{prefix}-totals__item-label {
min-width: 160px;
}
.#{prefix}-totals__item-amount {
flex: 1 1 auto;
text-align: right;
}
.#{prefix}-paragraph {
margin-bottom: 20px;
}
.#{prefix}-paragraph__label {
color: #666;
}
.#{prefix}-paragraph__value {
/* Styles for values within the paragraph section */
}
block content
//- block head
div(class=`${prefix}-root`, style=`--invoice-primary-color: ${primaryColor}; --invoice-secondary-color: ${secondaryColor};`)
//- Header (includes big title, details and logo )
div(class=`${prefix}-header`)
//- Header details (includes big title and details )
div(class=`${prefix}-header-details`)
//- Title and company logo
h1(class=`${prefix}-big-title`) Invoice
//- Invoice details
div(class=`${prefix}-details`)
if showInvoiceNumber
div(class=`${prefix}-detail`)
div(class=`${prefix}-detail__label`) #{invoiceNumberLabel}
div(class=`${prefix}-detail__value`) #{invoiceNumber}
if showDateIssue
div(class=`${prefix}-detail`)
div(class=`${prefix}-detail__label`) #{dateIssueLabel}
div(class=`${prefix}-detail__value`) #{dateIssue}
if showDueDate
div(class=`${prefix}-detail`)
div(class=`${prefix}-detail__label`) #{dueDateLabel}
div(class=`${prefix}-detail__value`) #{dueDate}
//- Company logo
if showCompanyLogo && companyLogoUri
div(class=`${prefix}-logo-wrap`)
img(alt="Company logo", src=companyLogoUri)
//- Address section
div(class=`${prefix}-address-root`)
if showCompanyAddress
div(class=`${prefix}-address-from`)
div !{companyAddress}
if showCustomerAddress
div(class=`${prefix}-address-to`)
strong #{billedToLabel}
div !{customerAddress}
//- Invoice table
table(class=`${prefix}-table`)
thead
tr
th(class=`${prefix}-table__header`) #{lineItemLabel}
th(class=`${prefix}-table__header`) #{lineDescriptionLabel}
th(class=`${prefix}-table__header ${prefix}-table__header--right`) #{lineRateLabel}
th(class=`${prefix}-table__header ${prefix}-table__header--right`) #{lineTotalLabel}
tbody
each line in lines
tr
td(class=`${prefix}-table__cell`) #{line.item}
td(class=`${prefix}-table__cell`) #{line.description}
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.rate}
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.total}
//- Totals section
div(class=`${prefix}-totals`)
if showSubtotal
div(class=`${prefix}-totals__item ${prefix}-totals__item--border-gray`)
div(class=`${prefix}-totals__item-label`) #{subtotalLabel}
div(class=`${prefix}-totals__item-amount`) #{subtotal}
if showDiscount
div(class=`${prefix}-totals__item`)
div(class=`${prefix}-totals__item-label`) #{discountLabel}
div(class=`${prefix}-totals__item-amount`) #{discount}
if showTaxes
each tax in taxes
div(class=`${prefix}-totals__item`)
div(class=`${prefix}-totals__item-label`) #{tax.label}
div(class=`${prefix}-totals__item-amount`) #{tax.amount}
if showTotal
div(class=`${prefix}-totals__item ${prefix}-totals__item--border-dark ${prefix}-totals__item--font-weight-bold`)
div(class=`${prefix}-totals__item-label`) #{totalLabel}
div(class=`${prefix}-totals__item-amount`) #{total}
if showPaymentMade
div(class=`${prefix}-totals__item`)
div(class=`${prefix}-totals__item-label`) #{paymentMadeLabel}
div(class=`${prefix}-totals__item-amount`) #{paymentMade}
if showBalanceDue
div(class=`${prefix}-totals__item ${prefix}-totals__item--border-dark ${prefix}-totals__item--font-weight-bold`)
div(class=`${prefix}-totals__item-label`) #{balanceDueLabel}
div(class=`${prefix}-totals__item-amount`) #{balanceDue}
//- Footer section
if showTermsConditions && termsConditions
div(class=`${prefix}-paragraph`)
if termsConditionsLabel
div(class=`${prefix}-paragraph__label`) #{termsConditionsLabel}
div(class=`${prefix}-paragraph__value`) #{termsConditions}
if showStatement && statement
div(class=`${prefix}-paragraph`)
if statementLabel
div(class=`${prefix}-paragraph__label`) #{statementLabel}
div(class=`${prefix}-paragraph__value`) #{statement}

View File

@@ -1,67 +1,192 @@
extends ../PaperTemplateLayout.pug
block head
style
if (isRtl)
include ../../css/modules/payment-rtl.css
else
include ../../css/modules/payment.css
- var prefix = 'bp3';
style.
.#{prefix}-root{
color: #111;
padding: 24px 30px;
font-size: 12px;
position: relative;
box-shadow: inset 0 4px 0px 0 var(--invoice-primary-color);
}
.#{prefix}-header{
box-sizing: border-box;
display: flex;
flex-flow: wrap;
flex: 0 0 auto;
-webkit-box-align: start;
align-items: start;
-webkit-box-pack: start;
justify-content: flex-start;
gap: 10px;
}
.#{prefix}-header-details{
flex: 1;
display: flex;
flex-direction: column;
align-items: stretch;
gap: 20px;
flex: 1 1 0%;
}
.#{prefix}-big-title{
font-size: 30px;
margin: 0;
line-height: 1;
font-weight: 500;
color: #333;
}
.#{prefix}-logo-wrap img {
width: 100%;
height: 100%;
max-width: 260px;
max-height: 100px;
}
.#{prefix}-terms-list{
display: flex;
flex-direction: column;
gap: 4px;
margin-bottom: 24px;
}
.#{prefix}-terms-item{
display: flex;
flex-direction: row;
gap: 12px;
}
.#{prefix}-terms-item__label{
min-width: 120px;
color: #333;
}
.#{prefix}-addresses{
box-sizing: border-box;
display: flex;
flex-flow: wrap;
align-items: flex-start;
justify-content: flex-start;
gap: 10px;
margin-bottom: 24px;
}
.#{prefix}-addresses > * {
flex: 1 1;
}
.#{prefix}-address__label{
}
.#{prefix}-table {
width: 100%;
border-collapse: collapse;
text-align: left;
font-size: inherit;
}
.#{prefix}-table__header {
font-weight: 400;
border-bottom: 1px solid #000;
padding: 2px 10px;
color: #333;
}
.#{prefix}-table__header:first-of-type{
padding-left: 0;
}
.#{prefix}-table__header:last-of-type{
padding-right: 0;
}
.#{prefix}-table__header--right {
text-align: right;
}
.#{prefix}-table__cell {
border-bottom: 1px solid #F6F6F6;
padding: 12px 10px;
}
.#{prefix}-table__cell:first-of-type{
padding-left: 0;
}
.#{prefix}-table__cell:last-of-type {
padding-right: 0;
}
.#{prefix}-table__cell--right {
text-align: right;
}
.#{prefix}-totals {
display: flex;
flex-direction: column;
margin-left: auto;
width: 300px;
margin-bottom: 24px;
}
.#{prefix}-totals__item {
display: flex;
padding: 4px 0;
}
.#{prefix}-totals__item--gray-border {
border-bottom: 1px solid #DADADA;
}
.#{prefix}-totals__item--dark-border {
border-bottom: 1px solid #000;
}
.#{prefix}-totals__item--bold {
font-weight: bold;
}
.#{prefix}-totals__item-label {
min-width: 160px;
}
.#{prefix}-totals__item-amount {
flex: 1 1 auto;
text-align: right;
}
block content
div.payment
div.payment__header
div.paper
h1.title #{__("payment.paper.payment_receipt")}
if paymentReceive.paymentReceiveNo
span.paymentNumber #{paymentReceive.paymentReceiveNo}
div(class=`${prefix}-root`)
//- Header (includes big title, details and logo )
div(class=`${prefix}-header`)
//- Header details (includes big title and details )
div(class=`${prefix}-header-details`)
div(class=`${prefix}-big-title`) Payment
div(class=`${prefix}-terms-list`)
if showPaymentReceivedNumber
div(class=`${prefix}-terms-item`)
div(class=`${prefix}-terms-item__label`) #{paymentReceivedNumberLabel}
div(class=`${prefix}-terms-item__value`) #{paymentReceivedNumebr}
div.organization
h3.title #{organizationName}
if organizationEmail
span.email #{organizationEmail}
if showPaymentReceivedDate
div(class=`${prefix}-terms-item`)
div(class=`${prefix}-terms-item__label`) #{paymentReceivedDateLabel}
div(class=`${prefix}-terms-item__value`) #{paymentReceivedDate}
div.payment__received-amount
div.label #{__('payment.paper.amount_received')}
div.amount #{paymentReceive.formattedAmount}
if showCompanyLogo && companyLogoUri
div(class=`${prefix}-logo-wrap`)
img(src=companyLogoUri alt="Company Logo")
div(class=`${prefix}-addresses`)
if showCompanyAddress
div(class=`${prefix}-address-from`)
div !{companyAddress}
div.payment__meta
div.payment__meta-item.payment__meta-item--billed-to
span.label #{__("payment.paper.billed_to")}
span.value #{paymentReceive.customer.displayName}
if showCustomerAddress
div(class=`${prefix}-address-to`)
strong #{billedToLabel}
div !{customerAddress}
div.payment__meta-item.payment__meta-item--payment-date
span.label #{__("payment.paper.payment_date")}
span.value #{paymentReceive.formattedPaymentDate}
table(class=`${prefix}-table`)
thead
tr
th(class=`${prefix}-table__header`) Invoice #
th(class=`${prefix}-table__header ${prefix}-table__header--right`) Invoice Amount
th(class=`${prefix}-table__header ${prefix}-table__header--right`) Paid Amount
div.payment__table
table
thead
tr
th.item #{__("payment.paper.invoice_number")}
th.date #{__("payment.paper.invoice_date")}
th.invoiceAmount #{__("payment.paper.invoice_amount")}
th.paymentAmount #{__("payment.paper.payment_amount")}
tbody
each entry in paymentReceive.entries
tr
td.item=entry.invoice.invoiceNo
td.date=entry.invoice.invoiceDateFormatted
td.invoiceAmount=entry.invoice.totalFormatted
td.paymentAmount=entry.invoice.paymentAmountFormatted
tbody
each line in lines
tr
td(class=`${prefix}-table__cell`) #{line.invoiceNumber}
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.invoiceAmount}
td(class=`${prefix}-table__cell ${prefix}-table__cell--right`) #{line.paidAmount}
div.payment__table-after
div.payment__table-total
table
tbody
tr.payment-amount
td #{__('payment.paper.payment_amount')}
td #{paymentReceive.formattedAmount}
tr.blanace-due
td #{__('payment.paper.balance_due')}
td #{paymentReceive.customer.closingBalance}
div(class=`${prefix}-totals`)
if showSubtotal
div(class=`${prefix}-totals__item ${prefix}-totals__item--gray-border`)
div(class=`${prefix}-totals__item-label`) #{subtotalLabel}
div(class=`${prefix}-totals__item-amount`) #{subtotal}
div.payment__footer
if paymentReceive.statement
div.payment__notes
h3 #{__("payment.paper.statement")}
p #{paymentReceive.statement}
if showTotal
div(class=`${prefix}-totals__item ${prefix}-totals__item--dark-border`)
div(class=`${prefix}-totals__item-label`) #{totalLabel}
div(class=`${prefix}-totals__item-amount`) #{total}

View File

@@ -1,77 +1,215 @@
extends ../PaperTemplateLayout.pug
block head
style
if (isRtl)
include ../../css/modules/receipt-rtl.css
else
include ../../css/modules/receipt.css
- var prefix = 'bc'
style.
.#{prefix}-root {
color: #000;
padding: 24px 30px;
font-size: 12px;
position: relative;
box-shadow: inset 0 4px 0px 0 var(--invoice-primary-color);
}
.#{prefix}-header{
box-sizing: border-box;
display: flex;
flex-flow: wrap;
flex: 0 0 auto;
align-items: start;
justify-content: flex-start;
gap: 10px;
}
.#{prefix}-header-details{
flex: 1;
display: flex;
flex-direction: column;
align-items: stretch;
gap: 20px;
flex: 1 1 0%;
}
.#{prefix}-logo-wrap img {
width: 100%;
height: 100%;
max-width: 260px;
max-height: 100px;
}
.#{prefix}-big-title {
font-size: 30px;
margin: 0;
line-height: 1;
font-weight: 500;
color: #333;
}
.#{prefix}-terms-list {
display: flex;
flex-direction: column;
gap: 4px;
margin-bottom: 24px;
}
.#{prefix}-terms-item {
display: flex;
flex-direction: row;
gap: 12px;
}
.#{prefix}-terms-item__label {
min-width: 120px;
color: #333;
}
.#{prefix}-terms-item__value {}
.#{prefix}-address-section {
box-sizing: border-box;
display: flex;
flex-flow: wrap;
-webkit-box-align: flex-start;
align-items: flex-start;
-webkit-box-pack: start;
justify-content: flex-start;
gap: 10px;
margin-bottom: 24px;
}
.#{prefix}-address-section > * {
flex: 1 1 auto;
}
.#{prefix}-address {}
.#{prefix}-table {
width: 100%;
border-collapse: collapse;
text-align: left;
font-size: inherit;
}
.#{prefix}-table__header {
font-weight: 400;
border-bottom: 1px solid #000;
padding: 2px 10px;
color: #333;
}
.#{prefix}-table__header:first-of-type{
padding-left: 0;
}
.#{prefix}-table__header:last-of-type{
padding-right: 0;
}
.#{prefix}-table__header--right {
text-align: right;
}
.#{prefix}-table__cell {
border-bottom: 1px solid #F6F6F6;
padding: 12px 10px;
}
.#{prefix}-table__cell:first-of-type{
padding-left: 0;
}
.#{prefix}-table__cell:last-of-type {
padding-right: 0;
}
.#{prefix}-table__cell--right {
text-align: right;
}
.#{prefix}-totals {
display: flex;
flex-direction: column;
margin-left: auto;
width: 300px;
margin-bottom: 24px;
}
.#{prefix}-totals__line {
display: flex;
padding: 4px 0;
}
.#{prefix}-totals__line--gray-border {
border-bottom: 1px solid #DADADA;
}
.#{prefix}-totals__line--dark-border {
border-bottom: 1px solid #000;
}
.#{prefix}-totals__line__label {
min-width: 160px;
}
.#{prefix}-totals__line__amount {
flex: 1 1 auto;
text-align: right;
}
.#{prefix}-statement {
margin-bottom: 20px;
}
.#{prefix}-statement__label {}
.#{prefix}-statement__value {}
block content
div.receipt
div.receipt__header
div.paper
h1.title #{__("receipt.paper.receipt")}
span.receiptNumber #{saleReceipt.receiptNumber}
//- block head
div(class=`${prefix}-root`, style=`--invoice-primary-color: ${primaryColor}; --invoice-secondary-color: ${secondaryColor};`)
div.organization
h3.title #{organizationName}
//- Header (includes big title, details and logo )
div(class=`${prefix}-header`)
//- Header details (includes big title and details )
div(class=`${prefix}-header-details`)
//- Title and company logo
h1(class=`${prefix}-big-title`) Receipt
div.receipt__receipt-amount
div.label #{__('receipt.paper.receipt_amount')}
div.amount #{saleReceipt.formattedAmount}
//- Terms List
div(class=`${prefix}-terms-list`)
if showReceiptNumber
div(class=`${prefix}-terms-item`)
span(class=`${prefix}-terms-item__label`)= receiptNumberLabel
span(class=`${prefix}-terms-item__value`)= receiptNumber
div.receipt__meta
div.receipt__meta-item.receipt__meta-item--billed-to
span.label #{__("receipt.paper.billed_to")}
span.value #{saleReceipt.customer.displayName}
if showReceiptDate
div(class=`${prefix}-terms-item`)
span(class=`${prefix}-terms-item__label`)= receiptDateLabel
span(class=`${prefix}-terms-item__value`)= receiptDate
div.receipt__meta-item.receipt__meta-item--invoice-date
span.label #{__("receipt.paper.receipt_date")}
span.value #{saleReceipt.formattedReceiptDate}
//- Company logo
if showCompanyLogo && companyLogoUri
div(class=`${prefix}-logo-wrap`)
img(src=companyLogoUri alt=`Company Logo`)
if saleReceipt.receiptNumber
div.receipt__meta-item.receipt__meta-item--invoice-number
span.label #{__("receipt.paper.receipt_number")}
span.value #{saleReceipt.receiptNumber}
//- Address Section
div(class=`${prefix}-address-section`)
if showCompanyAddress
div(class=`${prefix}-address-from`)
div !{companyAddress}
div.receipt__table
table
thead
tr
th.item #{__("item_entry.paper.item_name")}
th.rate #{__("item_entry.paper.rate")}
th.quantity #{__("item_entry.paper.quantity")}
th.total #{__("item_entry.paper.total")}
tbody
each entry in saleReceipt.entries
tr
td.item=entry.item.name
td.rate=entry.rate
td.quantity=entry.quantity
td.total=entry.amount
div.receipt__table-after
div.receipt__table-total
table
tbody
tr.total
td #{__('receipt.paper.total')}
td #{saleReceipt.formattedAmount}
tr.payment-amount
td #{__('receipt.paper.payment_amount')}
td #{saleReceipt.formattedAmount}
tr.blanace-due
td #{__('receipt.paper.balance_due')}
td #{'$0'}
if showCustomerAddress
div(class=`${prefix}-address-to`)
strong #{billedToLabel}
div !{customerAddress}
div.receipt__footer
if saleReceipt.statement
div.receipt__conditions
h3 #{__("receipt.paper.statement")}
p #{saleReceipt.statement}
//- Table Section
table(class=`${prefix}-table`)
thead(class=`${prefix}-table__header`)
tr
th(class=`${prefix}-table__header`) Item
th(class=`${prefix}-table__header`) Description
th(class=`${prefix}-table__header ${prefix}-table__header--right`) Rate
th(class=`${prefix}-table__header ${prefix}-table__header--right`) Total
tbody
each line in lines
tr(class=`${prefix}-table__row`)
td(class=`${prefix}-table__cell`)= line.item
td(class=`${prefix}-table__cell`)= line.description
td(class=`${prefix}-table__cell${prefix}-table__cell--right`)= line.rate
td(class=`${prefix}-table__cell${prefix}-table__cell--right`)= line.total
if saleReceipt.receiptMessage
div.receipt__notes
h3 #{__("receipt.paper.notes")}
p #{saleReceipt.receiptMessage}
//- Totals Section
div(class=`${prefix}-totals`)
if showSubtotal
div(class=`${prefix}-totals__line ${prefix}-totals__line--gray-border`)
span(class=`${prefix}-totals__line__label`)= subtotalLabel
span(class=`${prefix}-totals__line__amount`)= subtotal
if showTotal
div(class=`${prefix}-totals__line ${prefix}-totals__line--dark-border`)
span(class=`${prefix}-totals__line__label`)= totalLabel
span(class=`${prefix}-totals__line__amount`)= total
//- Customer Note Section
if showCustomerNote && customerNote
div(class=`${prefix}-statement`)
div(class=`${prefix}-statement__label`)= customerNoteLabel
div(class=`${prefix}-statement__value`)= customerNote
//- Terms & Conditions Section
if showTermsConditions && termsConditions
div(class=`${prefix}-statement`)
div(class=`${prefix}-statement__label`)= termsConditionsLabel
div(class=`${prefix}-statement__value`)= termsConditions

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

@@ -250,10 +250,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

@@ -96,13 +96,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 +139,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 +158,6 @@ export default class ItemsController extends BaseController {
.optional()
.isString()
.trim()
.escape()
.isLength({ max: DATATYPES_LENGTH.TEXT }),
check('active').optional().isBoolean().toBoolean(),
@@ -184,7 +179,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(),

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

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

View File

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

View File

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

View File

@@ -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

@@ -111,14 +111,15 @@ export default class BillsPayments extends BaseController {
check('vendor_id').exists().isNumeric().toInt(),
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
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({ min: 1 }),
check('entries').exists().isArray(),
check('entries.*.index').optional().isNumeric().toInt(),
check('entries.*.bill_id').exists().isNumeric().toInt(),
check('entries.*.payment_amount').exists().isNumeric().toFloat(),

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

@@ -27,6 +27,7 @@ import GetCreditNoteAssociatedAppliedInvoices from '@/services/CreditNotes/GetCr
import GetRefundCreditTransaction from '@/services/CreditNotes/GetRefundCreditNoteTransaction';
import GetCreditNotePdf from '../../../services/CreditNotes/GetCreditNotePdf';
import { ACCEPT_TYPE } from '@/interfaces/Http';
import { GetCreditNoteState } from '@/services/CreditNotes/GetCreditNoteState';
/**
* Credit notes controller.
* @service
@@ -81,6 +82,9 @@ export default class PaymentReceivesController extends BaseController {
@Inject()
creditNotePdf: GetCreditNotePdf;
@Inject()
getCreditNoteStateService: GetCreditNoteState;
/**
* Router constructor.
*/
@@ -105,6 +109,12 @@ export default class PaymentReceivesController extends BaseController {
this.asyncMiddleware(this.newCreditNote),
this.handleServiceErrors
);
router.get(
'/state',
CheckPolicies(CreditNoteAction.View, AbilitySubject.CreditNote),
this.asyncMiddleware(this.getCreditNoteState.bind(this)),
this.handleServiceErrors
);
// Get specific credit note.
router.get(
'/:id',
@@ -210,9 +220,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 +238,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()
@@ -239,6 +246,9 @@ export default class PaymentReceivesController extends BaseController {
check('attachments').isArray().optional(),
check('attachments.*.key').exists().isString(),
// Pdf template id.
check('pdf_template_id').optional({ nullable: true }).isNumeric().toInt(),
];
}
@@ -736,6 +746,23 @@ export default class PaymentReceivesController extends BaseController {
}
};
private getCreditNoteState = async (
req: Request,
res: Response,
next: NextFunction
) => {
const { tenantId } = req;
try {
const data = await this.getCreditNoteStateService.getCreditNoteState(
tenantId
);
return res.status(200).send({ data });
} catch (error) {
next(error);
}
};
/**
* Handles service errors.
* @param {Error} error

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;
@@ -95,6 +95,12 @@ export default class PaymentReceivesController extends BaseController {
asyncMiddleware(this.getPaymentReceiveInvoices.bind(this)),
this.handleServiceErrors
);
router.get(
'/state',
CheckPolicies(PaymentReceiveAction.View, AbilitySubject.PaymentReceive),
this.getPaymentReceivedState.bind(this),
this.handleServiceErrors
);
router.get(
'/:id',
CheckPolicies(PaymentReceiveAction.View, AbilitySubject.PaymentReceive),
@@ -150,16 +156,16 @@ export default class PaymentReceivesController extends BaseController {
check('customer_id').exists().isNumeric().toInt(),
check('exchange_rate').optional().isFloat({ gt: 0 }).toFloat(),
check('amount').exists().isNumeric().toFloat(),
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(),
check('entries').isArray({ min: 1 }),
check('entries').isArray({}),
check('entries.*.id').optional({ nullable: true }).isNumeric().toInt(),
check('entries.*.index').optional().isNumeric().toInt(),
check('entries.*.invoice_id').exists().isNumeric().toInt(),
@@ -167,6 +173,9 @@ export default class PaymentReceivesController extends BaseController {
check('attachments').isArray().optional(),
check('attachments.*.key').exists().isString(),
// Pdf template id.
check('pdf_template_id').optional({ nullable: true }).isNumeric().toInt(),
];
}
@@ -176,7 +185,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 +238,7 @@ export default class PaymentReceivesController extends BaseController {
try {
const storedPaymentReceive =
await this.paymentReceiveApplication.createPaymentReceive(
await this.paymentReceiveApplication.createPaymentReceived(
tenantId,
paymentReceive,
user
@@ -377,7 +385,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
);
@@ -389,6 +397,29 @@ export default class PaymentReceivesController extends BaseController {
}
}
/**
*
* @async
* @param {Request} req -
* @param {Response} res -
*/
private async getPaymentReceivedState(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
try {
const data = await this.paymentReceiveApplication.getPaymentReceivedState(
tenantId
);
return res.status(200).send({ data });
} catch (error) {
next(error);
}
}
/**
* Retrieve the given payment receive details.
* @async
@@ -405,7 +436,7 @@ export default class PaymentReceivesController extends BaseController {
try {
const { paymentReceive, entries } =
await this.PaymentReceivesPages.getPaymentReceiveEditPage(
await this.PaymentsReceivedPages.getPaymentReceiveEditPage(
tenantId,
paymentReceiveId,
user

View File

@@ -51,7 +51,7 @@ export default class SalesEstimatesController extends BaseController {
router.post(
'/:id/approve',
CheckPolicies(SaleEstimateAction.Edit, AbilitySubject.SaleEstimate),
[this.validateSpecificEstimateSchema],
[...this.validateSpecificEstimateSchema],
this.validationResult,
asyncMiddleware(this.approveSaleEstimate.bind(this)),
this.handleServiceErrors
@@ -59,7 +59,7 @@ export default class SalesEstimatesController extends BaseController {
router.post(
'/:id/reject',
CheckPolicies(SaleEstimateAction.Edit, AbilitySubject.SaleEstimate),
[this.validateSpecificEstimateSchema],
[...this.validateSpecificEstimateSchema],
this.validationResult,
asyncMiddleware(this.rejectSaleEstimate.bind(this)),
this.handleServiceErrors
@@ -105,6 +105,12 @@ export default class SalesEstimatesController extends BaseController {
asyncMiddleware(this.deleteEstimate.bind(this)),
this.handleServiceErrors
);
router.get(
'/state',
CheckPolicies(SaleEstimateAction.View, AbilitySubject.SaleEstimate),
this.getSaleEstimateState.bind(this),
this.handleServiceErrors
);
router.get(
'/:id',
CheckPolicies(SaleEstimateAction.View, AbilitySubject.SaleEstimate),
@@ -155,7 +161,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(),
@@ -168,10 +174,7 @@ export default class SalesEstimatesController extends BaseController {
check('entries.*.item_id').exists().isNumeric().toInt(),
check('entries.*.quantity').exists().isNumeric().toInt(),
check('entries.*.rate').exists().isNumeric().toFloat(),
check('entries.*.description')
.optional({ nullable: true })
.trim()
.escape(),
check('entries.*.description').optional({ nullable: true }).trim(),
check('entries.*.discount')
.optional({ nullable: true })
.isNumeric()
@@ -181,12 +184,15 @@ 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(),
// Pdf template id.
check('pdf_template_id').optional({ nullable: true }).isNumeric().toInt(),
];
}
@@ -546,6 +552,23 @@ export default class SalesEstimatesController extends BaseController {
}
};
private getSaleEstimateState = async (
req: Request,
res: Response,
next: NextFunction
) => {
const { tenantId } = req;
try {
const data = await this.saleEstimatesApplication.getSaleEstimateState(
tenantId
);
return res.status(200).send({ data });
} catch (error) {
next(error);
}
};
/**
* Handles service errors.
* @param {Error} error

View File

@@ -130,6 +130,12 @@ export default class SaleInvoicesController extends BaseController {
this.asyncMiddleware(this.getInvoicePaymentTransactions),
this.handleServiceErrors
);
router.get(
'/state',
CheckPolicies(SaleInvoiceAction.View, AbilitySubject.SaleInvoice),
asyncMiddleware(this.getSaleInvoiceState.bind(this)),
this.handleServiceErrors
);
router.get(
'/:id',
CheckPolicies(SaleInvoiceAction.View, AbilitySubject.SaleInvoice),
@@ -138,6 +144,7 @@ export default class SaleInvoicesController extends BaseController {
asyncMiddleware(this.getSaleInvoice.bind(this)),
this.handleServiceErrors
);
router.get(
'/',
CheckPolicies(SaleInvoiceAction.View, AbilitySubject.SaleInvoice),
@@ -200,12 +207,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(),
@@ -224,14 +231,10 @@ export default class SaleInvoicesController extends BaseController {
.optional({ nullable: true })
.isNumeric()
.toFloat(),
check('entries.*.description')
.optional({ nullable: true })
.trim()
.escape(),
check('entries.*.description').optional({ nullable: true }).trim(),
check('entries.*.tax_code')
.optional({ nullable: true })
.trim()
.escape()
.isString(),
check('entries.*.tax_rate_id')
.optional({ nullable: true })
@@ -259,6 +262,14 @@ export default class SaleInvoicesController extends BaseController {
.optional({ nullable: true })
.isNumeric()
.toFloat(),
// Pdf template id.
check('pdf_template_id').optional({ nullable: true }).isNumeric().toInt(),
// Payment methods.
check('payment_methods').optional({ nullable: true }).isArray(),
check('payment_methods.*.payment_integration_id').exists().toInt(),
check('payment_methods.*.enable').exists().isBoolean(),
];
}
@@ -449,6 +460,24 @@ export default class SaleInvoicesController extends BaseController {
return res.status(200).send({ saleInvoice });
}
}
private async getSaleInvoiceState(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
try {
const data = await this.saleInvoiceApplication.getSaleInvoiceState(
tenantId
);
return res.status(200).send({ data });
} catch (error) {
next(error);
}
}
/**
* Retrieve paginated sales invoices with custom view metadata.
* @param {Request} req

View File

@@ -108,6 +108,12 @@ export default class SalesReceiptsController extends BaseController {
this.handleServiceErrors,
this.dynamicListService.handlerErrorsToResponse
);
router.get(
'/state',
CheckPolicies(SaleReceiptAction.View, AbilitySubject.SaleReceipt),
asyncMiddleware(this.getSaleReceiptState.bind(this)),
this.handleServiceErrors
);
router.get(
'/:id',
CheckPolicies(SaleReceiptAction.View, AbilitySubject.SaleReceipt),
@@ -130,8 +136,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(),
@@ -148,18 +154,20 @@ export default class SalesReceiptsController extends BaseController {
.optional({ nullable: true })
.isNumeric()
.toInt(),
check('entries.*.description')
.optional({ nullable: true })
.trim()
.escape(),
check('entries.*.description').optional({ nullable: true }).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(),
// Pdf template id.
check('pdf_template_id').optional({ nullable: true }).isNumeric().toInt(),
];
}
@@ -367,6 +375,30 @@ export default class SalesReceiptsController extends BaseController {
}
}
/**
*
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
public async getSaleReceiptState(
req: Request,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
// Retrieves receipt in pdf format.
try {
const data = await this.saleReceiptsApplication.getSaleReceiptState(
tenantId
);
return res.status(200).send({ data });
} catch (error) {
next(error);
}
}
/**
* Sale receipt notification via SMS.
* @param {Request} req

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

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

View File

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

View File

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

View File

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

View File

@@ -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

@@ -1,9 +1,10 @@
import { NextFunction, Router, Request, Response } from 'express';
import { Inject, Service } from 'typedi';
import Container, { Inject, Service } from 'typedi';
import { PlaidApplication } from '@/services/Banking/Plaid/PlaidApplication';
import BaseController from '../BaseController';
import { LemonSqueezyWebhooks } from '@/services/Subscription/LemonSqueezyWebhooks';
import { PlaidWebhookTenantBootMiddleware } from '@/services/Banking/Plaid/PlaidWebhookTenantBootMiddleware';
import { StripeWebhooksController } from '../StripeIntegration/StripeWebhooksController';
@Service()
export class Webhooks extends BaseController {
@@ -24,6 +25,8 @@ export class Webhooks extends BaseController {
router.post('/lemon', this.lemonWebhooks.bind(this));
router.use(Container.get(StripeWebhooksController).router());
return router;
}
@@ -35,7 +38,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,12 @@ 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';
import { StripeIntegrationController } from './controllers/StripeIntegration/StripeIntegrationController';
import { ShareLinkController } from './controllers/ShareLink/ShareLinkController';
import { PublicSharableLinkController } from './controllers/ShareLink/PublicSharableLinkController';
import { PdfTemplatesController } from './controllers/PdfTemplates/PdfTemplatesController';
import { PaymentServicesController } from './controllers/PaymentServices/PaymentServicesController';
export default () => {
const app = Router();
@@ -80,6 +86,11 @@ 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());
app.use(
'/payment-links',
Container.get(PublicSharableLinkController).router()
);
// - Dashboard routes.
// ---------------------------
@@ -145,10 +156,22 @@ export default () => {
dashboard.use('/import', Container.get(ImportController).router());
dashboard.use('/export', Container.get(ExportController).router());
dashboard.use('/attachments', Container.get(AttachmentsController).router());
dashboard.use(
'/stripe_integration',
Container.get(StripeIntegrationController).router()
);
dashboard.use(
'/pdf-templates',
Container.get(PdfTemplatesController).router()
);
dashboard.use(
'/payment-services',
Container.get(PaymentServicesController).router()
);
dashboard.use('/', Container.get(ProjectTasksController).router());
dashboard.use('/', Container.get(ProjectTimesController).router());
dashboard.use('/', Container.get(WarehousesItemController).router());
dashboard.use('/', Container.get(ShareLinkController).router());
dashboard.use('/dashboard', Container.get(DashboardController).router());
dashboard.use('/', Container.get(Miscellaneous).router());

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,36 @@ 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')
.throwIfNotFound();
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,5 +236,40 @@ 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,
},
/**
* Stripe Payment Integration.
*/
stripePayment: {
secretKey: process.env.STRIPE_PAYMENT_SECRET_KEY || '',
publishableKey: process.env.STRIPE_PAYMENT_PUBLISHABLE_KEY || '',
clientId: process.env.STRIPE_PAYMENT_CLIENT_ID || '',
redirectTo: process.env.STRIPE_PAYMENT_REDIRECT_URL || '',
webhooksSecret: process.env.STRIPE_PAYMENT_WEBHOOKS_SECRET || '',
},
};

View File

@@ -0,0 +1,91 @@
export const SALE_INVOICE_CREATED = 'Sale invoice created';
export const SALE_INVOICE_EDITED = 'Sale invoice edited';
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';
export const PDF_TEMPLATE_CREATED = 'PDF template created';
export const PDF_TEMPLATE_EDITED = 'PDF template edited';
export const PDF_TEMPLATE_DELETED = 'PDF template deleted';
export const PDF_TEMPLATE_ASSIGNED_DEFAULT = 'PDF template assigned as default';
export const PAYMENT_METHOD_EDITED = 'Payment method edited';
export const PAYMENT_METHOD_DELETED = 'Payment method deleted';
export const INVOICE_PAYMENT_LINK_GENERATED = 'Invoice payment link generated';
export const STRIPE_INTEGRAION_CONNECTED = 'Stripe integration oauth2 connected';
// # Event Groups
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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -12,8 +12,7 @@ export default class SeedAccounts extends TenantSeeder {
description: this.i18n.__(account.description),
currencyCode: this.tenant.metadata.baseCurrency,
seededAt: new Date(),
})
);
}));
return knex('accounts').then(async () => {
// Inserts seed entries.
return knex('accounts').insert(data);

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

@@ -9,6 +9,39 @@ export const TaxPayableAccount = {
predefined: 1,
};
export const UnearnedRevenueAccount = {
name: 'Unearned Revenue',
slug: 'unearned-revenue',
account_type: 'other-current-liability',
parent_account_id: null,
code: '50005',
active: true,
index: 1,
predefined: true,
};
export const PrepardExpenses = {
name: 'Prepaid Expenses',
slug: 'prepaid-expenses',
account_type: 'other-current-asset',
parent_account_id: null,
code: '100010',
active: true,
index: 1,
predefined: true,
};
export const StripeClearingAccount = {
name: 'Stripe Clearing',
slug: 'stripe-clearing',
account_type: 'other-current-asset',
parent_account_id: null,
code: '100020',
active: true,
index: 1,
predefined: true,
}
export default [
{
name: 'Bank Account',
@@ -323,4 +356,6 @@ export default [
index: 1,
predefined: 0,
},
UnearnedRevenueAccount,
PrepardExpenses,
];

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,24 @@ 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>;
oldMainUncategorizedTransaction: IUncategorizedCashflowTransaction;
oldUncategorizedTransactions: Array<IUncategorizedCashflowTransaction>;
trx: Knex.Transaction;
}
@@ -164,11 +168,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,9 @@ export interface ICreditNote {
localAmount?: number;
branchId?: number;
warehouseId: number;
createdAt?: Date,
createdAt?: Date;
termsConditions: string;
note: string;
}
export enum CreditNoteAction {
@@ -130,7 +132,9 @@ export interface ICreditNoteOpenedPayload {
trx: Knex.Transaction;
}
export interface ICreditNotesQueryDTO {}
export interface ICreditNotesQueryDTO {
filterQuery?: (query: any) => void;
}
export interface ICreditNotesQueryDTO extends IDynamicListFilter {
page: number;
@@ -256,3 +260,61 @@ export type ICreditNoteGLCommonEntry = Pick<
| 'debit'
| 'branchId'
>;
export interface CreditNotePdfTemplateAttributes {
// # Primary color
primaryColor: string;
secondaryColor: string;
// # Company logo
showCompanyLogo: boolean;
companyLogo: string;
// # Company name
companyName: string;
// # Customer Address
showCustomerAddress: boolean;
customerAddress: string;
// # Company address
showCompanyAddress: boolean;
companyAddress: string;
billedToLabel: string;
total: string;
totalLabel: string;
showTotal: boolean;
subtotal: string;
subtotalLabel: string;
showSubtotal: boolean;
showCustomerNote: boolean;
customerNote: string;
customerNoteLabel: string;
showTermsConditions: boolean;
termsConditions: string;
termsConditionsLabel: string;
lines: Array<{
item: string;
description: string;
rate: string;
quantity: string;
total: string;
}>;
showCreditNoteNumber: boolean;
creditNoteNumberLabel: string;
creditNoteNumebr: string;
creditNoteDate: string;
showCreditNoteDate: boolean;
creditNoteDateLabel: string;
}
export interface ICreditNoteState {
defaultTemplateId: number;
}

View File

@@ -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

@@ -40,7 +40,7 @@ export interface ILedgerEntry {
date: Date | string;
transactionType: string;
transactionSubType: string;
transactionSubType?: string;
transactionId: number;

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