Compare commits

...

140 Commits

Author SHA1 Message Date
Ahmed Bouhuolia
5df454dd30 chore: bump packages version to v0.10.2 2023-10-02 23:29:21 +02:00
Ahmed Bouhuolia
07628ddc37 fix(server): add missing method in ItemEntry model. 2023-10-02 23:27:19 +02:00
Ahmed Bouhuolia
69afa07e3b fix(webapp): Disable tax rates from item entries editor table on services do not support tax rates 2023-10-02 23:27:05 +02:00
Ahmed Bouhuolia
b1a043f699 chore(server): add package-lock.json file 2023-09-27 19:16:02 +02:00
Ahmed Bouhuolia
9eaff0785b chore: update CHANGELOG.md file 2023-09-25 17:14:07 +02:00
Ahmed Bouhuolia
b3a97ed5d5 chore: dump packages versions 2023-09-25 15:34:10 +02:00
Ahmed Bouhuolia
1aaa65edd2 Merge pull request #242 from bigcapitalhq/abouhuolia/big-62-running-tenants-dbs-migration-on-migration-docker-container
fix: Running tenants migration on docker migration container
2023-09-25 15:31:54 +02:00
Ahmed Bouhuolia
efebf424d1 fix: running tenants migration on docker migration container 2023-09-25 15:31:09 +02:00
Ahmed Bouhuolia
597973d5d1 chore: update @blueprintjs-formik/select package. 2023-09-24 12:30:01 +02:00
Ahmed Bouhuolia
ee6bc822c9 Merge pull request #240 from bigcapitalhq/BIG-61-make-tables-header-sticy
fix: Table headers sticky for all reports.
2023-09-24 12:15:51 +02:00
ElforJani13
07e78ebd6a Sticky table header for all reports 2023-09-24 04:58:16 +02:00
Ahmed Bouhuolia
8c04a6205b Merge pull request #197 from bigcapitalhq/dependabot/npm_and_yarn/mongoose-5.13.20
chore(deps): bump mongoose from 5.13.15 to 5.13.20
2023-09-23 14:43:25 +02:00
Ahmed Bouhuolia
d54ea68d46 Merge pull request #199 from bigcapitalhq/dependabot/npm_and_yarn/packages/webapp/word-wrap-1.2.4
chore(deps): bump word-wrap from 1.2.3 to 1.2.4 in /packages/webapp
2023-09-23 14:43:12 +02:00
Ahmed Bouhuolia
f42c8031c2 Merge pull request #200 from bigcapitalhq/dependabot/npm_and_yarn/word-wrap-1.2.4
chore(deps): bump word-wrap from 1.2.3 to 1.2.4
2023-09-23 14:42:49 +02:00
Ahmed Bouhuolia
5261d332b7 Merge pull request #204 from bigcapitalhq/tax-compliance
feat: [wip] tax rates service
2023-09-23 14:41:32 +02:00
Ahmed Bouhuolia
f1e7d0fc44 fix(server): sales tax liability summary report 2023-09-23 14:40:21 +02:00
Ahmed Bouhuolia
1d1049043e fix(webapp): invoice total currency should be dynamic 2023-09-23 14:19:59 +02:00
Ahmed Bouhuolia
1148fef9ad fix(server): tax tracking on sale invoices 2023-09-23 14:19:42 +02:00
Ahmed Bouhuolia
92ac0dbd25 fix(webapp): code cleanup 2023-09-22 15:25:56 +02:00
Ahmed Bouhuolia
ce41845bd7 fix(server): pull-request nodes 2023-09-22 15:23:33 +02:00
Ahmed Bouhuolia
eaf72d1608 fix(server): tax percentage calculation of tax sales liability summary report 2023-09-20 19:25:37 +02:00
Ahmed Bouhuolia
ac336f9878 feat(webapp): add tax compund tag to tax rates 2023-09-20 19:25:07 +02:00
Ahmed Bouhuolia
746c80c564 fix(server): tax rate could be zero. 2023-09-20 17:22:58 +02:00
Ahmed Bouhuolia
601434b107 feat: avoid invoice writes GL entry with zero amount 2023-09-20 17:22:39 +02:00
Ahmed Bouhuolia
5aaa33e585 feat(webapp): add activate/inactivate tax rate buttons on details drawer 2023-09-20 15:06:27 +02:00
Ahmed Bouhuolia
d48d864a5f fix(sever): tax rates cell. 2023-09-20 00:43:55 +02:00
Ahmed Bouhuolia
453df2ac4e fix(server): Validation of activate/inacitvate tax rates 2023-09-20 00:42:34 +02:00
Ahmed Bouhuolia
73ceeaee46 fix(server): [Sales Tax Liability Report] filter non-transactions tax rates 2023-09-20 00:42:03 +02:00
Ahmed Bouhuolia
1b4b656419 feat(server): order tax rates by name 2023-09-18 18:57:54 +02:00
Ahmed Bouhuolia
df823c0bfe feat(webapp): tax rates empty state 2023-09-18 18:57:24 +02:00
Ahmed Bouhuolia
e91d7b0cff feat(webapp): tax rate form validation errors 2023-09-18 11:23:50 +02:00
Ahmed Bouhuolia
aa52e7d02c feat(server): soft deleting tax rates 2023-09-18 10:15:55 +02:00
Ahmed Bouhuolia
4e53d08497 feat(server): wip activate/inactivate tax rate 2023-09-18 01:38:38 +02:00
Ahmed Bouhuolia
2356921f27 feat(webapp): wip tax rate form dialog 2023-09-18 01:35:53 +02:00
Ahmed Bouhuolia
a0a5d00be3 chore: Add Patreon link for funding 2023-09-15 01:34:45 +02:00
Ahmed Bouhuolia
fbd74c559b feat(server): tweak the tax rate transformer 2023-09-14 23:36:15 +02:00
Ahmed Bouhuolia
8a64198433 feat(webapp): wip tax rates management 2023-09-14 23:35:54 +02:00
Ahmed Bouhuolia
b98b73ad98 feat(webapp): invoice tax rate 2023-09-11 23:17:27 +02:00
Ahmed Bouhuolia
6abae43c6f feat: tax rate transformer 2023-09-11 20:46:46 +02:00
Ahmed Bouhuolia
7657337c4f feat: sales tax report query 2023-09-08 19:49:46 +02:00
Ahmed Bouhuolia
983ceb5cc6 feat: sale invoice model tax attributes 2023-09-06 14:01:40 +02:00
Ahmed Bouhuolia
ac072d29fc feat(server): wip sale invoice tax rate GL entries 2023-09-04 18:39:49 +02:00
Ahmed Bouhuolia
17e055db5e chore: update README.md file 2023-09-03 01:35:10 +02:00
Ahmed Bouhuolia
b49a021506 feat: contact us section to README.md file 2023-09-03 01:32:06 +02:00
Ahmed Bouhuolia
b49b45fb43 feat: wip sales tax summry report 2023-09-02 01:52:07 +02:00
Ahmed Bouhuolia
bb7cf41e3e feat: add sales tax summary report to reports list 2023-09-02 01:51:28 +02:00
Ahmed Bouhuolia
801ea5dfdb feat: wip sales tax summary report 2023-09-02 01:50:24 +02:00
Ahmed Bouhuolia
eb03a38553 feat(webapp): wip sales tax summary report 2023-09-01 20:50:22 +02:00
Ahmed Bouhuolia
0852feecbf fix(server): avoid display total row if no tax rates on sales tax report 2023-09-01 20:48:23 +02:00
Ahmed Bouhuolia
54dcde657f feat(webapp): wip sales tax liability summary report 2023-09-01 01:39:16 +02:00
Ahmed Bouhuolia
5bb95eeb1a feat: wip sales tax liability summary report 2023-08-31 21:39:59 +02:00
Ahmed Bouhuolia
6baec8dd96 feat(server): wip sales tax liability summary report 2023-08-31 02:19:18 +02:00
Ahmed Bouhuolia
6535424d0f feat(server): wip sale invoice tax rates 2023-08-29 19:12:19 +02:00
Ahmed Bouhuolia
09d73db20f Merge branch 'develop' into tax-compliance 2023-08-29 15:09:52 +02:00
Ahmed Bouhuolia
7f746b96c8 chore: remove /data directory from git ignored dirs 2023-08-29 03:05:19 +02:00
Ahmed Bouhuolia
ed2bca6b74 chore: dump CHANGELOG for 0.9.12 2023-08-29 02:54:03 +02:00
Ahmed Bouhuolia
f9d5a3c69a Merge pull request #231 from bigcapitalhq/fix-filter-transactions-date-format
fix(server): date format of filtering transactions by date range
2023-08-29 02:42:35 +02:00
Ahmed Bouhuolia
84445d4bac fix(server): date format of filtering transactions by date range 2023-08-29 02:41:40 +02:00
Ahmed Bouhuolia
75d8864aae Merge remote-tracking branch 'refs/remotes/origin/develop' into develop 2023-08-28 21:03:23 +02:00
Ahmed Bouhuolia
cb6dab08d8 chore: remove un-used methods 2023-08-28 21:01:34 +02:00
Ahmed Bouhuolia
5112ef9b64 Merge pull request #230 from bigcapitalhq/abouhuolia/big-60-fromto-date-is-not-showing-up-on-inventory-items-details
fix: change the default from/date date value of reports
2023-08-28 20:59:02 +02:00
Ahmed Bouhuolia
a630e8a612 fix: change the default from/to dates of customers/vendors transactions 2023-08-28 20:53:52 +02:00
Ahmed Bouhuolia
4df63561cf fix(webapp): change the default from/to date values of reports 2023-08-27 16:00:54 +02:00
Ahmed Bouhuolia
c7a3bac44c fix(server): change the default from/date date value of reports 2023-08-27 15:50:52 +02:00
Ahmed Bouhuolia
251c54be60 Merge pull request #229 from bigcapitalhq/abouhuolia/big-45-receivablepayable-again-report-issue
fix AP/AR aging summary issue
2023-08-27 00:58:17 +02:00
Ahmed Bouhuolia
5dec4a7df0 chore(server): document methods 2023-08-27 00:56:14 +02:00
Ahmed Bouhuolia
0d57ca88bf fix(server): avoid return total row on aging summary reports if no customers 2023-08-27 00:45:12 +02:00
Ahmed Bouhuolia
b9be83dc2b fix(webapp): add validation to aging summary customize form 2023-08-27 00:44:37 +02:00
Ahmed Bouhuolia
321de4d327 refactor(webapp): AR/AP aging summary table columns 2023-08-26 02:16:35 +02:00
Ahmed Bouhuolia
4e66d1ac98 feat(server): AP/AR aging summary table transformer 2023-08-24 23:24:05 +02:00
Ahmed Bouhuolia
b5fe5a8bcb Merge pull request #227 from bigcapitalhq/fix-spelling-a-char
Fix spelling words start with `A` letter
2023-08-22 22:56:23 +02:00
Ahmed Bouhuolia
508054b594 chore: spelling 2023-08-22 22:49:58 +02:00
Ahmed Bouhuolia
34efd58f34 Merge branch 'develop' into fix-spelling-a-char 2023-08-22 22:49:39 +02:00
Ahmed Bouhuolia
f898acdb8b Merge pull request #225 from bigcapitalhq/abouhuolia/big-59-transaction-type-of-credit-note-and-vendor-credit-are-not
fix(server): Transaction type of credit note and vendor credit are not defined on account transactions
2023-08-22 13:54:08 +02:00
Ahmed Bouhuolia
f7b53692f5 fix(server): Transaction type of credit note and vendor credit are not defined on account transactions 2023-08-22 13:51:15 +02:00
Ahmed Bouhuolia
b665d05526 chore: remove not used files 2023-08-21 11:39:01 +02:00
Ahmed Bouhuolia
de5300b186 Merge pull request #224 from bigcapitalhq/abouhuolia/big-54-specific-items-filter-on-purchasessells-by-items-reports
fix(webapp): filter by customers, vendors and items in reports do not work
2023-08-20 23:20:22 +02:00
Ahmed Bouhuolia
abc5631ac2 chore(webapp): document functions 2023-08-20 23:17:06 +02:00
Ahmed Bouhuolia
9e7b906c86 Merge pull request #221 from bigcapitalhq/abouhuolia/big-56-should-not-write-gl-entries-when-save-transaction-as-draft
fix(server): shouldn't write GL entries when save transaction as draft.
2023-08-20 23:04:31 +02:00
Ahmed Bouhuolia
d5decbbd0b fix(webap): sales by items query state from query string 2023-08-20 22:39:37 +02:00
Ahmed Bouhuolia
fbeb489128 fix(webapp): filter by customers, vendors and items in reports do not work 2023-08-20 01:59:44 +02:00
Ahmed Bouhuolia
5bf8a9e0ff chore: update CONTRIBUTING.md file 2023-08-17 23:06:06 +02:00
Ahmed Bouhuolia
68fa5cf5c5 Merge remote-tracking branch 'refs/remotes/origin/develop' into develop 2023-08-17 23:02:43 +02:00
Ahmed Bouhuolia
b1662c3175 chore: update CONTRIBUTING.md file 2023-08-17 23:02:13 +02:00
Ahmed Bouhuolia
0fcee0eaa7 fix(server): wirte GL entries only when publish transaction 2023-08-17 21:49:07 +02:00
Ahmed Bouhuolia
5b2be2ac19 fix(server): shouldn't write GL entries when save transaction as draft. 2023-08-16 23:05:39 +02:00
Ahmed Bouhuolia
5bb80fde34 Merge pull request #220 from bigcapitalhq/all-contributors/add-KalliopiPliogka
docs: add KalliopiPliogka as a contributor for bug
2023-08-16 21:39:26 +02:00
Ahmed Bouhuolia
58f90a0bcd Merge pull request #219 from KalliopiPliogka/bill-message-without-bill-number
Update index.json
2023-08-16 21:36:11 +02:00
allcontributors[bot]
e1a3510f0b docs: update .all-contributorsrc [skip ci] 2023-08-16 19:36:08 +00:00
allcontributors[bot]
172eea0ad1 docs: update README.md [skip ci] 2023-08-16 19:36:07 +00:00
Kalliopi Pliogka
74c4418549 Update BillForm.tsx
Removed the injected number value where the deleted keywords were used.
2023-08-16 22:30:31 +03:00
Kalliopi Pliogka
6b6e19f53b Update index.json
Fixed bill message. Now, bill message is showing without the bill number.
2023-08-16 21:39:00 +03:00
Ahmed Bouhuolia
01f7effc71 dix(webapp): create quick customer/vendor (#206) 2023-08-14 18:38:02 +02:00
Ahmed Bouhuolia
d1121f0b81 feat(server): wip tax rate on sale invoice service 2023-08-14 14:59:10 +02:00
Ahmed Bouhuolia
a7644e6481 feat: tax rates on sale invoice service 2023-08-11 21:08:30 +02:00
Ahmed Bouhuolia
d6f56568a3 feat: tax rates crud service 2023-08-11 16:00:39 +02:00
Ahmed Bouhuolia
04d134806b feat(server): wip tax rates service 2023-08-11 01:31:52 +02:00
Ahmed Bouhuolia
26c6ca9e36 refactor: split the services to multiple service classes (#202) 2023-08-10 20:29:39 +02:00
Ahmed Bouhuolia
ffef627dc3 Merge remote-tracking branch 'refs/remotes/origin/develop' into develop 2023-07-23 22:38:13 +02:00
Ahmed Bouhuolia
f105980f08 chore: update CHANGELOG.md 2023-07-23 22:37:44 +02:00
Ahmed Bouhuolia
efad38fcdc Merge pull request #195 from bigcapitalhq/api-rate-env-vars
fix: expose the rate limit to the env variables
2023-07-23 20:17:51 +02:00
Ahmed Bouhuolia
d84568e43a Merge pull request #201 from bigcapitalhq/all-contributors/add-suhaibaffan
docs: add suhaibaffan as a contributor for code
2023-07-23 20:16:56 +02:00
allcontributors[bot]
ed6517c0e1 docs: update .all-contributorsrc [skip ci] 2023-07-23 18:16:22 +00:00
allcontributors[bot]
0fd256c801 docs: update README.md [skip ci] 2023-07-23 18:16:21 +00:00
Ahmed Bouhuolia
f0285560aa Merge pull request #198 from suhaibaffan/#149-restart-crashed-docker-containers
Added restart policy to docker compose files.
2023-07-23 20:13:48 +02:00
Ahmed Bouhuolia
7a33f79268 chore: update mysql docker container restart policy 2023-07-23 20:05:34 +02:00
Ahmed Bouhuolia
ef5ef647d4 chore: change docker restart policy to unless-stopped 2023-07-23 19:54:55 +02:00
dependabot[bot]
339559d830 chore(deps): bump word-wrap from 1.2.3 to 1.2.4
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-20 06:12:05 +00:00
dependabot[bot]
22e4d757e4 chore(deps): bump word-wrap from 1.2.3 to 1.2.4 in /packages/webapp
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-20 06:08:24 +00:00
Suhaib Affan
ce62a0524c Added restart policy to docker compose files. 2023-07-19 20:19:56 -04:00
dependabot[bot]
3b98e8de80 chore(deps): bump mongoose from 5.13.15 to 5.13.20
Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.13.15 to 5.13.20.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Automattic/mongoose/compare/5.13.15...5.13.20)

---
updated-dependencies:
- dependency-name: mongoose
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-18 21:00:25 +00:00
Ahmed Bouhuolia
278c8a01c5 Merge remote-tracking branch 'refs/remotes/origin/develop' into develop 2023-07-18 20:03:31 +02:00
Ahmed Bouhuolia
f22dc9a18b fix(webapp): assign currency code of customer/vendor to the transaction form. 2023-07-18 20:02:45 +02:00
Ahmed Bouhuolia
b224a2c313 Merge pull request #196 from bigcapitalhq/fix-loading-status-on-financial-reports
fix(webapp): show loading message of cost computing job on financial reports
2023-07-17 01:51:02 +02:00
Ahmed Bouhuolia
d4a933ef18 fix(webapp): show loading message of cost computing job on financial reports 2023-07-17 01:41:13 +02:00
Josh Soref
1411f64cf6 spelling: average
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:47 -04:00
Josh Soref
2c9739ac91 spelling: avatar
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:47 -04:00
Josh Soref
e214b86a62 spelling: available
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:47 -04:00
Josh Soref
b01b7010c0 spelling: authorized
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:47 -04:00
Josh Soref
e7cd035206 spelling: authenticate
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:47 -04:00
Josh Soref
f82b78c4eb spelling: attachment
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:47 -04:00
Josh Soref
847b4380be spelling: attaches
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:47 -04:00
Josh Soref
271011cb3c spelling: async
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:47 -04:00
Josh Soref
b6d8766173 spelling: associate
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:47 -04:00
Josh Soref
efffdc021b spelling: appropriate
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:47 -04:00
Josh Soref
01dd0ffb8c spelling: application
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:47 -04:00
Josh Soref
d910985b37 spelling: another
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:47 -04:00
Josh Soref
d5799bf720 spelling: amount
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:47 -04:00
Josh Soref
56cc1da034 spelling: already
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:47 -04:00
Josh Soref
ef9b4ebad6 spelling: after
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:47 -04:00
Josh Soref
29af788dcd spelling: adjustment
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:46 -04:00
Josh Soref
b2510145dc spelling: actual
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:46 -04:00
Josh Soref
5f3a309a8f spelling: activate
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:46 -04:00
Josh Soref
e2fdc13b3e spelling: accumulated
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:46 -04:00
Josh Soref
34cd21cced spelling: accumulate
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:46 -04:00
Josh Soref
0e589ace82 spelling: accounts
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:46 -04:00
Josh Soref
f46f595e96 spelling: accountant
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:46 -04:00
Josh Soref
53ef940b05 spelling: account
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:46 -04:00
Josh Soref
65495775d4 spelling: accessible
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:46 -04:00
Josh Soref
1cf11e020f spelling: abstract
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-06-27 09:34:46 -04:00
510 changed files with 29196 additions and 6847 deletions

View File

@@ -42,6 +42,24 @@
"contributions": [
"bug"
]
},
{
"login": "suhaibaffan",
"name": "Suhaib Affan",
"avatar_url": "https://avatars.githubusercontent.com/u/18115937?v=4",
"profile": "https://github.com/suhaibaffan",
"contributions": [
"code"
]
},
{
"login": "KalliopiPliogka",
"name": "Kalliopi Pliogka",
"avatar_url": "https://avatars.githubusercontent.com/u/81677549?v=4",
"profile": "https://github.com/KalliopiPliogka",
"contributions": [
"bug"
]
}
],
"contributorsPerLine": 7,

13
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: Bigcapital # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

7
.gitignore vendored
View File

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

View File

@@ -2,6 +2,45 @@
All notable changes to Bigcapital server-side will be in this file.
# [0.10.1] - 25-09-2023
* Fix: Running tenants migration on Docker migration container by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/242
# [0.10.0] - 24-09-2023
* Added: Tax rates service by @abouolia @elforjani13 in https://github.com/bigcapitalhq/bigcapital/pull/204
* Added: Sales Tax Liability Summary report by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/204
* Added: Tax rates tracking with sale invoices by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/204
* fix(webapp): Table headers sticky for all reports. by @elforjani13 in https://github.com/bigcapitalhq/bigcapital/pull/240
* chore(deps): bump word-wrap from 1.2.3 to 1.2.4 by @dependabot in https://github.com/bigcapitalhq/bigcapital/pull/200
* chore(deps): bump word-wrap from 1.2.3 to 1.2.4 in /packages/webapp by @dependabot in https://github.com/bigcapitalhq/bigcapital/pull/199
* chore(deps): bump mongoose from 5.13.15 to 5.13.20 by @dependabot in https://github.com/bigcapitalhq/bigcapital/pull/197
# [0.9.12] - 29-08-2023
* Refactor: split the services to multiple service classes. (by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/202)
* Fix: create quick customer/vendor by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/206
* Fix: typo in bill success message without bill number by @KalliopiPliogka in https://github.com/bigcapitalhq/bigcapital/pull/219
* Fix: AP/AR aging summary issue by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/229
* Fix: shouldn't write GL entries when save transaction as draft. by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/221
* Fix: Transaction type of credit note and vendor credit are not defined on account transactions by @abouolia in
* Fix: date format of filtering transactions by date range by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/231
* Fix: change the default from/date date value of reports by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/230
* Fix: typos in words start with `A` letter by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/227
* Fix: filter by customers, vendors and items in reports do not work by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/224
https://github.com/bigcapitalhq/bigcapital/pull/225
# [0.9.11] - 23-07-2023
* added: Restart policy to docker compose files. by @suhaibaffan in https://github.com/bigcapitalhq/bigcapital/pull/198
* fix: Expose and expand the rate limit to the env variables by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/195
# [0.9.10] - 18-07-2023
* feat(e2e): E2E onboarding process by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/176
* fix(webapp): Show loading message of cost computing job on financial reports by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/196
* fix(webapp): Change the currency code of sales and purchases transactions with foreign contacts.
# [0.9.9] - 28-06-2023
* refactor: Customer and vendor select component by @abouolia in https://github.com/bigcapitalhq/bigcapital/pull/171
@@ -86,7 +125,7 @@ All notable changes to Bigcapital server-side will be in this file.
- fix: delete invoice transaction issue.
`@bigcapital/webapp`
- fix: general, accoutant and items preferences.
- fix: general, accountant and items preferences.
- fix: auto-increment sale invoices, estiamtes, credit notes, payments and manual journals.
- refactor: the setup organization form to use binded Formik components.

View File

@@ -7,6 +7,7 @@ Please read through this document before submitting any issues or pull requests
## Sections
- [General Instructions](#general-instructions)
- [Local Setup Prerequisites](#local-setup-prerequisites)
- [Contribute to Backend](#contribute-to-backend)
- [Contribute to Frontend](#contribute-to-frontend)
- [Other Ways to Contribute](#other-ways-to-contribute)
@@ -31,9 +32,18 @@ Contributions via pull requests are much appreciated. Once the approach is agree
---
## Local Setup Prerequisites
- The application currently supports **Node.js v14.x**. Please ensure that you are using this version of Node.js when developing. (use [nvm](https://github.com/nvm-sh/nvm#installing-and-updating) to switch between node versions)
## Contribute to Backend
- Clone the `bigcapital` repository and `cd` into `bigcapital` directory.
- Create `.env` file by copying `.env.example` file to `.env`. (The ``.env.example`` file has all the necessary values of variables to start development directly).
```
cp .env.example .env
```
- Install all npm dependencies of the monorepo, you don't have to change directory to the `backend` package. just hit these command on root directory and it will install dependencies of all packages.
```

View File

@@ -48,6 +48,12 @@ Bigcapital is a smart and open-source accounting and inventory software, Bigcapi
Please see [Releases](https://github.com/bigcapitalhq/bigcapital/releases) for more information what has changed recently.
# Contact us
Meet our sales team for any commercial inquiries.
<a target="_blank" href="https://cal.com/ahmed-bouhuolia-ekk3ph/30min"><img src="https://cal.com/book-with-cal-dark.svg" alt="Book us with Cal.com"></a>
# Recognition
<a href="https://news.ycombinator.com/item?id=36118990">
@@ -69,9 +75,11 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<tbody>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/abouolia"><img src="https://avatars.githubusercontent.com/u/2197422?v=4?s=100" width="100px;" alt="Ahmed Bouhuolia"/><br /><sub><b>Ahmed Bouhuolia</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=abouolia" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/elforjani13"><img src="https://avatars.githubusercontent.com/u/39470382?v=4?s=100" width="100px;" alt="ElforJani13"/><br /><sub><b>ElforJani13</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=elforjani13" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://ameir.net"><img src="https://avatars.githubusercontent.com/u/374330?v=4?s=100" width="100px;" alt="Ameir Abdeldayem"/><br /><sub><b>Ameir Abdeldayem</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Aameir" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/elforjani13"><img src="https://avatars.githubusercontent.com/u/39470382?v=4?s=100" width="100px;" alt="ElforJani13"/><br /><sub><b>ElforJani13</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=elforjani13" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://scheibling.se"><img src="https://avatars.githubusercontent.com/u/24367830?v=4?s=100" width="100px;" alt="Lars Scheibling"/><br /><sub><b>Lars Scheibling</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Ascheibling" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/suhaibaffan"><img src="https://avatars.githubusercontent.com/u/18115937?v=4?s=100" width="100px;" alt="Suhaib Affan"/><br /><sub><b>Suhaib Affan</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=suhaibaffan" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/KalliopiPliogka"><img src="https://avatars.githubusercontent.com/u/81677549?v=4?s=100" width="100px;" alt="Kalliopi Pliogka"/><br /><sub><b>Kalliopi Pliogka</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3AKalliopiPliogka" title="Bug reports">🐛</a></td>
</tr>
</tbody>
</table>

View File

@@ -15,16 +15,22 @@ services:
- ./data/logs/nginx/:/var/log/nginx
- ./docker/certbot/certs/:/var/certs
ports:
- "${PUBLIC_PROXY_PORT:-80}:80"
- "${PUBLIC_PROXY_SSL_PORT:-443}:443"
- '${PUBLIC_PROXY_PORT:-80}:80'
- '${PUBLIC_PROXY_SSL_PORT:-443}:443'
tty: true
depends_on:
- server
- webapp
deploy:
restart_policy:
condition: unless-stopped
webapp:
container_name: bigcapital-webapp
image: ghcr.io/bigcapitalhq/webapp:latest
deploy:
restart_policy:
condition: unless-stopped
server:
container_name: bigcapital-server
@@ -37,6 +43,9 @@ services:
- mysql
- mongo
- redis
deploy:
restart_policy:
condition: unless-stopped
environment:
# Mail
- MAIL_HOST=${MAIL_HOST}
@@ -62,7 +71,7 @@ services:
# Authentication
- JWT_SECRET=${JWT_SECRET}
# MongoDB
# MongoDB
- MONGODB_DATABASE_URL=mongodb://mongo/bigcapital
# Application
@@ -83,16 +92,22 @@ services:
context: ./
dockerfile: docker/migration/Dockerfile
environment:
# Database
- DB_HOST=mysql
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- DB_CHARSET=${DB_CHARSET}
- SYSTEM_DB_NAME=${SYSTEM_DB_NAME}
# Tenants databases
- TENANT_DB_NAME_PERFIX=${TENANT_DB_NAME_PERFIX}
depends_on:
- mysql
mysql:
container_name: bigcapital-mysql
deploy:
restart_policy:
condition: unless-stopped
build:
context: ./docker/mariadb
environment:
@@ -106,7 +121,10 @@ services:
- '3306'
mongo:
container_name: bigcapital-mongo
container_name: bigcapital-mongo
deploy:
restart_policy:
condition: unless-stopped
build: ./docker/mongo
expose:
- '27017'
@@ -115,10 +133,13 @@ services:
redis:
container_name: bigcapital-redis
deploy:
restart_policy:
condition: unless-stopped
build:
context: ./docker/redis
expose:
- "6379"
- '6379'
volumes:
- redis:/data

View File

@@ -20,6 +20,9 @@ services:
- '3306'
ports:
- '3306:3306'
deploy:
restart_policy:
condition: unless-stopped
mongo:
build: ./docker/mongo
@@ -29,6 +32,9 @@ services:
- mongo:/var/lib/mongodb
ports:
- '27017:27017'
deploy:
restart_policy:
condition: unless-stopped
redis:
build:
@@ -37,6 +43,9 @@ services:
- "6379"
volumes:
- redis:/data
deploy:
restart_policy:
condition: unless-stopped
# Volumes
volumes:

View File

@@ -34,5 +34,7 @@ WORKDIR /app/packages/server
RUN git clone https://github.com/vishnubob/wait-for-it.git
# Once we listen the mysql port run the migration task.
CMD ["./wait-for-it/wait-for-it.sh", "mysql:3306", "--", "node", "./build/commands.js", "system:migrate:latest"]
ADD docker/migration/start.sh /
RUN chmod +x /start.sh
CMD ["/start.sh"]

View File

@@ -0,0 +1,5 @@
# Migrate the master system database.
./wait-for-it/wait-for-it.sh mysql:3306 -- node ./build/commands.js system:migrate:latest
# Migrate all tenants.
./wait-for-it/wait-for-it.sh mysql:3306 -- node ./build/commands.js tenants:migrate:latest

7
e2e/items.spec.ts Normal file
View File

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

13663
packages/server/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -58,6 +58,7 @@ export interface IAccountTransaction {
date: string | Date;
referenceType: string;
referenceTypeFormatted: string;
referenceId: number;
referenceNumber?: string;
@@ -76,6 +77,9 @@ export interface IAccountTransaction {
projectId?: number;
account?: IAccount;
taxRateId?: number;
taxRate?: number;
}
export interface IAccountResponse extends IAccount {}
@@ -149,3 +153,11 @@ export enum AccountAction {
VIEW = 'View',
TransactionsLocking = 'TransactionsLocking',
}
export enum TaxRateAction {
CREATE = 'Create',
EDIT = 'Edit',
DELETE = 'Delete',
VIEW = 'View',
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -48,6 +48,9 @@ export interface ILedgerEntry {
branchId?: number;
projectId?: number;
taxRateId?: number;
taxRate?: number;
entryId?: number;
createdAt?: Date;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,7 @@
import moment from 'moment';
import * as R from 'ramda';
import { includes, isFunction, isObject, isUndefined, omit } from 'lodash';
import { formatNumber } from 'utils';
import { isArrayLikeObject } from 'lodash/fp';
import { formatNumber, sortObjectKeysAlphabetically } from 'utils';
export class Transformer {
public context: any;
@@ -82,6 +81,7 @@ export class Transformer {
const normlizedItem = this.normalizeModelItem(item);
return R.compose(
sortObjectKeysAlphabetically,
this.transform,
R.when(this.hasExcludeAttributes, this.excludeAttributesTransformed),
this.includeAttributesTransformed

View File

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

View File

@@ -1,17 +1,16 @@
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import ItemSubscriber from '@/subscribers/Items/ItemSubscriber';
import InventoryAdjustmentsSubscriber from '@/subscribers/Inventory/InventoryAdjustment';
import BillWriteInventoryTransactionsSubscriber from '@/subscribers/Bills/WriteInventoryTransactions';
import PaymentSyncBillBalance from '@/subscribers/PaymentMades/PaymentSyncBillBalance';
import SaleReceiptInventoryTransactionsSubscriber from '@/subscribers/SaleReceipt/WriteInventoryTransactions';
import SaleInvoiceWriteInventoryTransactions from '@/subscribers/SaleInvoices/WriteInventoryTransactions';
import SaleInvoiceWriteGLEntriesSubscriber from '@/subscribers/SaleInvoices/WriteJournalEntries';
import SaleReceiptWriteGLEntriesSubscriber from '@/subscribers/SaleReceipt/WriteJournalEntries';
import PaymentReceiveSyncInvoices from '@/subscribers/PaymentReceive/PaymentReceiveSyncInvoices';
import CashflowTransactionSubscriber from '@/services/Cashflow/CashflowTransactionSubscriber';
import PaymentReceivesWriteGLEntriesSubscriber from '@/subscribers/PaymentReceive/WriteGLEntries';
import InventorySubscriber from '@/subscribers/Inventory/Inventory';
import SaleReceiptWriteGLEntriesSubscriber from '@/subscribers/SaleReceipt/WriteJournalEntries';
import { CustomerWriteGLOpeningBalanceSubscriber } from '@/services/Contacts/Customers/Subscribers/CustomerGLEntriesSubscriber';
import { VendorsWriteGLOpeningSubscriber } from '@/services/Contacts/Vendors/Subscribers/VendorGLEntriesSubscriber';
import SaleEstimateAutoSerialSubscriber from '@/subscribers/SaleEstimate/AutoIncrementSerial';
@@ -35,7 +34,7 @@ import PurgeAuthorizedUserOnceRoleMutate from '@/services/Roles/PurgeAuthorizedU
import SendSmsNotificationToCustomer from '@/subscribers/SaleInvoices/SendSmsNotificationToCustomer';
import SendSmsNotificationSaleReceipt from '@/subscribers/SaleReceipt/SendSmsNotificationToCustomer';
import SendSmsNotificationPaymentReceive from '@/subscribers/PaymentReceive/SendSmsNotificationToCustomer';
import SaleInvoiceWriteoffSubscriber from '@/services/Sales/SaleInvoiceWriteoffSubscriber';
import SaleInvoiceWriteoffSubscriber from '@/services/Sales/Invoices/SaleInvoiceWriteoffSubscriber';
import LandedCostSyncCostTransactionsSubscriber from '@/services/Purchases/LandedCost/LandedCostSyncCostTransactionsSubscriber';
import LandedCostInventoryTransactionsSubscriber from '@/services/Purchases/LandedCost/LandedCostInventoryTransactionsSubscriber';
import CreditNoteGLEntriesSubscriber from '@/services/CreditNotes/CreditNoteGLEntriesSubscriber';
@@ -66,7 +65,6 @@ import { ActivateWarehousesSubscriber } from '@/services/Warehouses/ActivateWare
import { ManualJournalWriteGLSubscriber } from '@/services/ManualJournals/ManualJournalGLEntriesSubscriber';
import { BillGLEntriesSubscriber } from '@/services/Purchases/Bills/BillGLEntriesSubscriber';
import { PaymentWriteGLEntriesSubscriber } from '@/services/Purchases/BillPayments/BillPaymentGLEntriesSubscriber';
import BranchesIntegrationsSubscribers from '@/services/Branches/EventsProvider';
import WarehousesIntegrationsSubscribers from '@/services/Warehouses/EventsProvider';
import { WarehouseTransferAutoIncrementSubscriber } from '@/services/Warehouses/WarehousesTransfers/WarehouseTransferAutoIncrementSubscriber';
@@ -81,6 +79,8 @@ import { ProjectBillableTasksSubscriber } from '@/services/Projects/Projects/Pro
import { ProjectBillableExpensesSubscriber } from '@/services/Projects/Projects/ProjectBillableExpenseSubscriber';
import { ProjectBillableBillSubscriber } from '@/services/Projects/Projects/ProjectBillableBillSubscriber';
import { SyncActualTimeTaskSubscriber } from '@/services/Projects/Times/SyncActualTimeTaskSubscriber';
import { SaleInvoiceTaxRateValidateSubscriber } from '@/services/TaxRates/subscribers/SaleInvoiceTaxRateValidateSubscriber';
import { WriteInvoiceTaxTransactionsSubscriber } from '@/services/TaxRates/subscribers/WriteInvoiceTaxTransactionsSubscriber';
export default () => {
return new EventPublisher();
@@ -88,7 +88,6 @@ export default () => {
export const susbcribers = () => {
return [
ItemSubscriber,
InventoryAdjustmentsSubscriber,
BillWriteInventoryTransactionsSubscriber,
PaymentSyncBillBalance,
@@ -188,5 +187,9 @@ export const susbcribers = () => {
ProjectBillableTasksSubscriber,
ProjectBillableExpensesSubscriber,
ProjectBillableBillSubscriber,
// Tax Rates
SaleInvoiceTaxRateValidateSubscriber,
WriteInvoiceTaxTransactionsSubscriber,
];
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -121,7 +121,7 @@ export default class CashflowTransaction extends TenantModel {
},
/**
* Cashflow transaction may has assocaited cashflow account.
* Cashflow transaction may has associated cashflow account.
*/
cashflowAccount: {
relation: Model.BelongsToOneRelation,

View File

@@ -33,7 +33,7 @@ export default class InventoryCostLotTracker extends TenantModel {
query.groupBy('item_id');
},
filterDateRange(query, startDate, endDate, type = 'day') {
const dateFormat = 'YYYY-MM-DD HH:mm:ss';
const dateFormat = 'YYYY-MM-DD';
const fromDate = moment(startDate).startOf(type).format(dateFormat);
const toDate = moment(endDate).endOf(type).format(dateFormat);

View File

@@ -1,9 +1,13 @@
import { Model, raw } from 'objection';
import { castArray, isEmpty } from 'lodash';
import { castArray } from 'lodash';
import moment from 'moment';
import TenantModel from 'models/TenantModel';
import { getTransactionTypeLabel } from '@/utils/transactions-types';
export default class InventoryTransaction extends TenantModel {
transactionId: number;
transactionType: string;
/**
* Table name
*/
@@ -23,27 +27,7 @@ export default class InventoryTransaction extends TenantModel {
* @return {string}
*/
get transcationTypeFormatted() {
return InventoryTransaction.getReferenceTypeFormatted(this.transactionType);
}
/**
* Reference type formatted.
*/
static getReferenceTypeFormatted(referenceType) {
const mapped = {
SaleInvoice: 'Sale invoice',
SaleReceipt: 'Sale receipt',
PaymentReceive: 'Payment receive',
Bill: 'Bill',
BillPayment: 'Payment made',
VendorOpeningBalance: 'Vendor opening balance',
CustomerOpeningBalance: 'Customer opening balance',
InventoryAdjustment: 'Inventory adjustment',
ManualJournal: 'Manual journal',
Journal: 'Manual journal',
LandedCost: 'transaction_type.landed_cost',
};
return mapped[referenceType] || '';
return getTransactionTypeLabel(this.transactionType);
}
/**
@@ -52,7 +36,7 @@ export default class InventoryTransaction extends TenantModel {
static get modifiers() {
return {
filterDateRange(query, startDate, endDate, type = 'day') {
const dateFormat = 'YYYY-MM-DD HH:mm:ss';
const dateFormat = 'YYYY-MM-DD';
const fromDate = moment(startDate).startOf(type).format(dateFormat);
const toDate = moment(endDate).endOf(type).format(dateFormat);

View File

@@ -1,9 +1,17 @@
import { Model } from 'objection';
import TenantModel from 'models/TenantModel';
import { getExlusiveTaxAmount, getInclusiveTaxAmount } from '@/utils/taxRate';
export default class ItemEntry extends TenantModel {
public taxRate: number;
public discount: number;
public quantity: number;
public rate: number;
public isInclusiveTax: number;
/**
* Table name.
* @returns {string}
*/
static get tableName() {
return 'items_entries';
@@ -11,17 +19,84 @@ export default class ItemEntry extends TenantModel {
/**
* Timestamps columns.
* @returns {string[]}
*/
get timestamps() {
return ['created_at', 'updated_at'];
}
/**
* Virtual attributes.
* @returns {string[]}
*/
static get virtualAttributes() {
return ['amount'];
return [
'amount',
'taxAmount',
'amountExludingTax',
'amountInclusingTax',
'total',
];
}
/**
* Item entry total.
* Amount of item entry includes tax and subtracted discount amount.
* @returns {number}
*/
get total() {
return this.amountInclusingTax;
}
/**
* Item entry amount.
* Amount of item entry that may include or exclude tax.
* @returns {number}
*/
get amount() {
return ItemEntry.calcAmount(this);
return this.quantity * this.rate;
}
/**
* Item entry amount including tax.
* @returns {number}
*/
get amountInclusingTax() {
return this.isInclusiveTax ? this.amount : this.amount + this.taxAmount;
}
/**
* Item entry amount excluding tax.
* @returns {number}
*/
get amountExludingTax() {
return this.isInclusiveTax ? this.amount - this.taxAmount : this.amount;
}
/**
* Discount amount.
* @returns {number}
*/
get discountAmount() {
return this.amount * (this.discount / 100);
}
/**
* Tag rate fraction.
* @returns {number}
*/
get tagRateFraction() {
return this.taxRate / 100;
}
/**
* Tax amount withheld.
* @returns {number}
*/
get taxAmount() {
return this.isInclusiveTax
? getInclusiveTaxAmount(this.amount, this.taxRate)
: getExlusiveTaxAmount(this.amount, this.taxRate);
}
static calcAmount(itemEntry) {
@@ -31,6 +106,9 @@ export default class ItemEntry extends TenantModel {
return discount ? total - total * discount * 0.01 : total;
}
/**
* Item entry relations.
*/
static get relationMappings() {
const Item = require('models/Item');
const BillLandedCostEntry = require('models/BillLandedCostEntry');
@@ -40,6 +118,7 @@ export default class ItemEntry extends TenantModel {
const SaleEstimate = require('models/SaleEstimate');
const ProjectTask = require('models/Task');
const Expense = require('models/Expense');
const TaxRate = require('models/TaxRate');
return {
item: {
@@ -86,6 +165,9 @@ export default class ItemEntry extends TenantModel {
},
},
/**
* Sale receipt reference.
*/
receipt: {
relation: Model.BelongsToOneRelation,
modelClass: SaleReceipt.default,
@@ -96,7 +178,7 @@ export default class ItemEntry extends TenantModel {
},
/**
*
* Project task reference.
*/
projectTaskRef: {
relation: Model.HasManyRelation,
@@ -108,7 +190,7 @@ export default class ItemEntry extends TenantModel {
},
/**
*
* Project expense reference.
*/
projectExpenseRef: {
relation: Model.HasManyRelation,
@@ -120,7 +202,7 @@ export default class ItemEntry extends TenantModel {
},
/**
*
* Project bill reference.
*/
projectBillRef: {
relation: Model.HasManyRelation,
@@ -130,6 +212,18 @@ export default class ItemEntry extends TenantModel {
to: 'bills.id',
},
},
/**
* Tax rate reference.
*/
tax: {
relation: Model.HasOneRelation,
modelClass: TaxRate.default,
join: {
from: 'items_entries.taxRateId',
to: 'tax_rates.id',
},
},
};
}
}

View File

@@ -1,11 +1,11 @@
import { mixin, Model, raw } from 'objection';
import { castArray } from 'lodash';
import { castArray, takeWhile } from 'lodash';
import moment from 'moment';
import TenantModel from 'models/TenantModel';
import ModelSetting from './ModelSetting';
import SaleInvoiceMeta from './SaleInvoice.Settings';
import CustomViewBaseModel from './CustomViewBaseModel';
import { DEFAULT_VIEWS } from '@/services/Sales/constants';
import { DEFAULT_VIEWS } from '@/services/Sales/Invoices/constants';
import ModelSearchable from './ModelSearchable';
export default class SaleInvoice extends mixin(TenantModel, [
@@ -13,6 +13,17 @@ export default class SaleInvoice extends mixin(TenantModel, [
CustomViewBaseModel,
ModelSearchable,
]) {
public taxAmountWithheld: number;
public balance: number;
public paymentAmount: number;
public exchangeRate: number;
public writtenoffAmount: number;
public creditedAmount: number;
public isInclusiveTax: boolean;
public writtenoffAt: Date;
public dueDate: Date;
public deliveredAt: Date;
/**
* Table name
*/
@@ -27,6 +38,9 @@ export default class SaleInvoice extends mixin(TenantModel, [
return ['created_at', 'updated_at'];
}
/**
*
*/
get pluralName() {
return 'asdfsdf';
}
@@ -36,35 +50,97 @@ export default class SaleInvoice extends mixin(TenantModel, [
*/
static get virtualAttributes() {
return [
'localAmount',
'dueAmount',
'balanceAmount',
'isDelivered',
'isOverdue',
'isPartiallyPaid',
'isFullyPaid',
'isPaid',
'isWrittenoff',
'isPaid',
'dueAmount',
'balanceAmount',
'remainingDays',
'overdueDays',
'filterByBranches',
'subtotal',
'subtotalLocal',
'subtotalExludingTax',
'taxAmountWithheldLocal',
'total',
'totalLocal',
'writtenoffAmountLocal',
];
}
/**
* Invoice amount in local currency.
* Invoice amount.
* @todo Sugger attribute to balance, we need to rename the balance to amount.
* @returns {number}
*/
get localAmount() {
return this.balance * this.exchangeRate;
get amount() {
return this.balance;
}
/**
* Invoice local written-off amount.
* Invoice amount in base currency.
* @returns {number}
*/
get localWrittenoffAmount() {
return this.writtenoffAmount * this.exchangeRate;
get amountLocal() {
return this.amount * this.exchangeRate;
}
/**
* Subtotal. (Tax inclusive) if the tax inclusive is enabled.
* @returns {number}
*/
get subtotal() {
return this.amount;
}
/**
* Subtotal in base currency. (Tax inclusive) if the tax inclusive is enabled.
* @returns {number}
*/
get subtotalLocal() {
return this.amountLocal;
}
/**
* Sale invoice amount excluding tax.
* @returns {number}
*/
get subtotalExludingTax() {
return this.isInclusiveTax
? this.subtotal - this.taxAmountWithheld
: this.subtotal;
}
/**
* Tax amount withheld in base currency.
* @returns {number}
*/
get taxAmountWithheldLocal() {
return this.taxAmountWithheld * this.exchangeRate;
}
/**
* Invoice total. (Tax included)
* @returns {number}
*/
get total() {
return this.isInclusiveTax
? this.subtotal
: this.subtotal + this.taxAmountWithheld;
}
/**
* Invoice total in local currency. (Tax included)
* @returns {number}
*/
get totalLocal() {
return this.total * this.exchangeRate;
}
/**
@@ -97,7 +173,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
* @return {boolean}
*/
get dueAmount() {
return Math.max(this.balance - this.balanceAmount, 0);
return Math.max(this.total - this.balanceAmount, 0);
}
/**
@@ -105,7 +181,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
* @return {boolean}
*/
get isPartiallyPaid() {
return this.dueAmount !== this.balance && this.dueAmount > 0;
return this.dueAmount !== this.total && this.dueAmount > 0;
}
/**
@@ -176,7 +252,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
* Filters the invoices between the given date range.
*/
filterDateRange(query, startDate, endDate, type = 'day') {
const dateFormat = 'YYYY-MM-DD HH:mm:ss';
const dateFormat = 'YYYY-MM-DD';
const fromDate = moment(startDate).startOf(type).format(dateFormat);
const toDate = moment(endDate).endOf(type).format(dateFormat);
@@ -333,6 +409,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
const PaymentReceiveEntry = require('models/PaymentReceiveEntry');
const Branch = require('models/Branch');
const Account = require('models/Account');
const TaxRateTransaction = require('models/TaxRateTransaction');
return {
/**
@@ -382,7 +459,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
},
/**
*
* Invoice may has associated cost transactions.
*/
costTransactions: {
relation: Model.HasManyRelation,
@@ -397,7 +474,7 @@ export default class SaleInvoice extends mixin(TenantModel, [
},
/**
*
* Invoice may has associated payment entries.
*/
paymentEntries: {
relation: Model.HasManyRelation,
@@ -420,6 +497,9 @@ export default class SaleInvoice extends mixin(TenantModel, [
},
},
/**
* Invoice may has associated written-off expense account.
*/
writtenoffExpenseAccount: {
relation: Model.BelongsToOneRelation,
modelClass: Account.default,
@@ -428,6 +508,21 @@ export default class SaleInvoice extends mixin(TenantModel, [
to: 'accounts.id',
},
},
/**
* Invoice may has associated tax rate transactions.
*/
taxes: {
relation: Model.HasManyRelation,
modelClass: TaxRateTransaction.default,
join: {
from: 'sales_invoices.id',
to: 'tax_rate_transactions.referenceId',
},
filter(builder) {
builder.where('reference_type', 'SaleInvoice');
},
},
};
}

View File

@@ -0,0 +1,48 @@
import { mixin, Model, raw } from 'objection';
import TenantModel from 'models/TenantModel';
import ModelSearchable from './ModelSearchable';
import SoftDeleteQueryBuilder from '@/collection/SoftDeleteQueryBuilder';
export default class TaxRate extends mixin(TenantModel, [ModelSearchable]) {
/**
* Table name
*/
static get tableName() {
return 'tax_rates';
}
/**
* Soft delete query builder.
*/
static get QueryBuilder() {
return SoftDeleteQueryBuilder;
}
/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt', 'updatedAt'];
}
/**
* Virtual attributes.
*/
static get virtualAttributes() {
return [];
}
/**
* Model modifiers.
*/
static get modifiers() {
return {};
}
/**
* Relationship mapping.
*/
static get relationMappings() {
return {};
}
}

View File

@@ -0,0 +1,56 @@
import { mixin, Model, raw } from 'objection';
import TenantModel from 'models/TenantModel';
import ModelSearchable from './ModelSearchable';
export default class TaxRateTransaction extends mixin(TenantModel, [
ModelSearchable,
]) {
/**
* Table name
*/
static get tableName() {
return 'tax_rate_transactions';
}
/**
* Timestamps columns.
*/
get timestamps() {
return [];
}
/**
* Virtual attributes.
*/
static get virtualAttributes() {
return [];
}
/**
* Model modifiers.
*/
static get modifiers() {
return {};
}
/**
* Relationship mapping.
*/
static get relationMappings() {
const TaxRate = require('models/TaxRate');
return {
/**
* Belongs to the tax rate.
*/
taxRate: {
relation: Model.BelongsToOneRelation,
modelClass: TaxRate.default,
join: {
from: 'tax_rate_transactions.taxRateId',
to: 'tax_rates.id',
},
},
};
}
}

View File

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

View File

@@ -2,6 +2,7 @@ import { Account } from 'models';
import TenantRepository from '@/repositories/TenantRepository';
import { IAccount } from '@/interfaces';
import { Knex } from 'knex';
import { TaxPayableAccount } from '@/database/seeds/data/accounts';
export default class AccountRepository extends TenantRepository {
/**
@@ -116,7 +117,7 @@ export default class AccountRepository extends TenantRepository {
if (!result) {
result = await this.model.query(trx).insertAndFetch({
name: this.i18n.__('account.accounts_receivable.currency', {
currency: currencyCode
currency: currencyCode,
}),
accountType: 'accounts-receivable',
currencyCode,
@@ -127,6 +128,29 @@ export default class AccountRepository extends TenantRepository {
return result;
};
/**
* Find or create tax payable account.
* @param {Record<string, string>}extraAttrs
* @param {Knex.Transaction} trx
* @returns
*/
async findOrCreateTaxPayable(
extraAttrs: Record<string, string> = {},
trx?: Knex.Transaction
) {
let result = await this.model
.query(trx)
.findOne({ slug: TaxPayableAccount.slug, ...extraAttrs });
if (!result) {
result = await this.model.query(trx).insertAndFetch({
...TaxPayableAccount,
...extraAttrs,
});
}
return result;
}
findOrCreateAccountsPayable = async (
currencyCode: string = '',
extraAttrs = {},

View File

@@ -1,6 +1,6 @@
import { Service, Inject } from 'typedi';
import events from '@/subscribers/events';
import { InventoryTransactionsWarehouses } from './AcountsTransactionsWarehouses';
import { InventoryTransactionsWarehouses } from './AccountsTransactionsWarehouses';
import { IBranchesActivatedPayload } from '@/interfaces';
@Service()

View File

@@ -1,10 +1,6 @@
import moment from 'moment';
import { castArray, sumBy, toArray } from 'lodash';
import { IBill, ISystemUser, IAccount } from '@/interfaces';
import { castArray } from 'lodash';
import JournalPoster from './JournalPoster';
import JournalEntry from './JournalEntry';
import { IExpense, IExpenseCategory } from '@/interfaces';
import { increment } from 'utils';
export default class JournalCommands {
journal: JournalPoster;
models: any;
@@ -16,7 +12,6 @@ export default class JournalCommands {
*/
constructor(journal: JournalPoster) {
this.journal = journal;
this.repositories = this.journal.repositories;
this.models = this.journal.models;
}

View File

@@ -79,7 +79,7 @@ export default class JournalPoster implements IJournalPoster {
}
/**
* Async initialize acccounts dependency graph.
* Async initialize accounts dependency graph.
* @private
* @returns {Promise<void>}
*/

View File

@@ -234,6 +234,9 @@ export default class Ledger implements ILedger {
entryId: entry.id,
branchId: entry.branchId,
projectId: entry.projectId,
taxRateId: entry.taxRateId,
taxRate: entry.taxRate,
};
}

View File

@@ -66,14 +66,14 @@ export class LedgerContactsBalanceStorage {
): Promise<(entry: ILedgerEntry) => boolean> => {
const { Account } = this.tenancy.models(tenantId);
const ARAPAcounts = await Account.query(trx).whereIn('accountType', [
const ARAPAccounts = await Account.query(trx).whereIn('accountType', [
ACCOUNT_TYPE.ACCOUNTS_RECEIVABLE,
ACCOUNT_TYPE.ACCOUNTS_PAYABLE,
]);
const ARAPAcountsIds = ARAPAcounts.map((a) => a.id);
const ARAPAccountsIds = ARAPAccounts.map((a) => a.id);
return (entry: ILedgerEntry) => {
return ARAPAcountsIds.indexOf(entry.accountId) !== -1;
return ARAPAccountsIds.indexOf(entry.accountId) !== -1;
};
};

View File

@@ -35,7 +35,7 @@ export default class LedgerStorageService {
// Saves the ledger entries.
this.ledgerEntriesService.saveEntries(tenantId, ledger, trx),
// Mutates the assocaited accounts balances.
// Mutates the associated accounts balances.
this.ledgerAccountsBalance.saveAccountsBalance(tenantId, ledger, trx),
// Mutates the associated contacts balances.
@@ -60,7 +60,7 @@ export default class LedgerStorageService {
// Deletes the ledger entries.
this.ledgerEntriesService.deleteEntries(tenantId, ledger, trx),
// Mutates the assocaited accounts balances.
// Mutates the associated accounts balances.
this.ledgerAccountsBalance.saveAccountsBalance(tenantId, ledger, trx),
// Mutates the associated contacts balances.

View File

@@ -108,7 +108,7 @@ export class LedegrAccountsStorage {
const { Account } = this.tenancy.models(tenantId);
const account = await Account.query(trx).findById(accountId);
// Filters the ledger entries by the current acount.
// Filters the ledger entries by the current account.
const accountLedger = ledger.whereAccountId(accountId);
// Retrieves the given tenant metadata.

View File

@@ -32,5 +32,8 @@ export const transformLedgerEntryToTransaction = (
projectId: entry.projectId,
costable: entry.costable,
taxRateId: entry.taxRateId,
taxRate: entry.taxRate,
};
};

View File

@@ -46,7 +46,7 @@ export default class AccountTransactionTransformer extends Transformer {
* @returns {string}
*/
public transactionTypeFormatted(transaction: IAccountTransaction) {
return transaction.referenceTypeFormatted;
return this.context.i18n.__(transaction.referenceTypeFormatted);
}
/**

View File

@@ -77,7 +77,7 @@ export class DeleteAccount {
// Authorize before delete account.
await this.authorize(tenantId, accountId, oldAccount);
// Deletes the account and assocaited transactions under UOW envirement.
// Deletes the account and associated transactions under UOW envirement.
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
// Triggers `onAccountDelete` event.
await this.eventPublisher.emitAsync(events.accounts.onDelete, {

View File

@@ -23,15 +23,6 @@ export class GetAccounts {
@Inject()
private transformer: TransformerInjectable;
/**
* Parsees accounts list filter DTO.
* @param filterDTO
* @returns
*/
private parseListFilterDTO(filterDTO) {
return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
}
/**
* Retrieve accounts datatable list.
* @param {number} tenantId
@@ -75,4 +66,13 @@ export class GetAccounts {
filterMeta: dynamicList.getResponseMeta(),
};
};
/**
* Parsees accounts list filter DTO.
* @param filterDTO
* @returns
*/
private parseListFilterDTO(filterDTO) {
return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
}
}

View File

@@ -1,9 +0,0 @@
export class AccountsReceivableRepository {
findOrCreateAccount = (currencyCode?: string) => {
};
}

View File

@@ -8,6 +8,7 @@ import { IBranchDeletedPayload, IBranchDeletePayload } from '@/interfaces';
import { CURDBranch } from './CRUDBranch';
import { BranchValidator } from './BranchValidate';
import { ERRORS } from './constants';
@Service()
export class DeleteBranch extends CURDBranch {
@Inject()

View File

@@ -14,8 +14,8 @@ export class ValidateBranchExistance {
/**
* Validate transaction branch id when the feature is active.
* @param {number} tenantId
* @param {number} branchId
* @param {number} tenantId
* @param {number} branchId
* @returns {Promise<void>}
*/
public validateTransactionBranchWhenActive = async (
@@ -32,18 +32,16 @@ export class ValidateBranchExistance {
/**
* Validate transaction branch id existance.
* @param {number} tenantId
* @param {number} branchId
* @param {number} tenantId
* @param {number} branchId
* @return {Promise<void>}
*/
public validateTransactionBranch = async (
tenantId: number,
branchId: number | null
) => {
//
this.validateBranchIdExistance(branchId);
//
await this.validateBranchExistance(tenantId, branchId);
};
@@ -62,7 +60,10 @@ export class ValidateBranchExistance {
* @param tenantId
* @param branchId
*/
public validateBranchExistance = async (tenantId: number, branchId: number) => {
public validateBranchExistance = async (
tenantId: number,
branchId: number
) => {
const { Branch } = this.tenancy.models(tenantId);
const branch = await Branch.query().findById(branchId);

View File

@@ -1,4 +1,4 @@
export * from './CashflowBranchesActviateSubscriber';
export * from './CashflowBranchesActivateSubscriber';
export * from './CreditNoteBranchesActivateSubscriber';
export * from './PaymentMadeBranchesActivateSubscriber';
export * from './PaymentReceiveBranchesActivateSubscriber';

View File

@@ -1,17 +1,12 @@
import { Service, Inject } from 'typedi';
import { includes, difference, camelCase, upperFirst } from 'lodash';
import { ACCOUNT_TYPE } from '@/data/AccountTypes';
import { IAccount, ICashflowTransactionLine } from '@/interfaces';
import { Service } from 'typedi';
import { includes, camelCase, upperFirst } from 'lodash';
import { IAccount } from '@/interfaces';
import { getCashflowTransactionType } from './utils';
import { ServiceError } from '@/exceptions';
import { CASHFLOW_TRANSACTION_TYPE, ERRORS } from './constants';
import HasTenancyService from '@/services/Tenancy/TenancyService';
@Service()
export class CommandCashflowValidator {
@Inject()
private tenancy: HasTenancyService;
/**
* Validates the lines accounts type should be cash or bank account.
* @param {IAccount} accounts -

View File

@@ -1,14 +0,0 @@
import { difference, includes } from 'lodash';
import { ICashflowTransactionLine } from '@/interfaces';
import { ServiceError } from '@/exceptions';
import { Inject, Service } from 'typedi';
import { CASHFLOW_TRANSACTION_TYPE, ERRORS } from './constants';
import { IAccount } from '@/interfaces';
import HasTenancyService from '@/services/Tenancy/TenancyService';
@Service()
export default class CommandCashflowTransaction {
@Inject()
private tenancy: HasTenancyService;
}

View File

@@ -6,7 +6,7 @@ import {
} from './constants';
/**
* Ensures the given transaction type to transformed to properiate format.
* Ensures the given transaction type to transformed to appropriate format.
* @param {string} type
* @returns {string}
*/

View File

@@ -16,16 +16,16 @@ import BaseCreditNotes from './CreditNotes';
@Service()
export default class CreateCreditNote extends BaseCreditNotes {
@Inject()
uow: UnitOfWork;
private uow: UnitOfWork;
@Inject()
itemsEntriesService: ItemsEntriesService;
private itemsEntriesService: ItemsEntriesService;
@Inject()
tenancy: HasTenancyService;
private tenancy: HasTenancyService;
@Inject()
eventPublisher: EventPublisher;
private eventPublisher: EventPublisher;
/**
* Creates a new credit note.

View File

@@ -1,5 +1,4 @@
import { Service, Inject } from 'typedi';
import HasTenancyService from '@/services/Tenancy/TenancyService';
import events from '@/subscribers/events';
import {
IApplyCreditToInvoicesCreatedPayload,
@@ -10,15 +9,12 @@ import CreditNoteApplySyncInvoicesCreditedAmount from './CreditNoteApplySyncInvo
@Service()
export default class CreditNoteApplySyncInvoicesCreditedAmountSubscriber {
@Inject()
tenancy: HasTenancyService;
@Inject()
syncInvoicesWithCreditNote: CreditNoteApplySyncInvoicesCreditedAmount;
private syncInvoicesWithCreditNote: CreditNoteApplySyncInvoicesCreditedAmount;
/**
* Attaches events with handlers.
*/
attach(bus) {
public attach(bus) {
bus.subscribe(
events.creditNote.onApplyToInvoicesCreated,
this.incrementAppliedInvoicesOnceCreditCreated

View File

@@ -1,5 +1,5 @@
import { Service, Inject } from 'typedi';
import Knex from 'knex';
import { Knex } from 'knex';
import { sumBy } from 'lodash';
import {
ICreditNote,
@@ -8,27 +8,31 @@ import {
ISaleInvoice,
} from '@/interfaces';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import PaymentReceiveService from '@/services/Sales/PaymentReceives/PaymentsReceives';
import UnitOfWork from '@/services/UnitOfWork';
import events from '@/subscribers/events';
import { PaymentReceiveValidators } from '../Sales/PaymentReceives/PaymentReceiveValidators';
import BaseCreditNotes from './CreditNotes';
import {
IApplyCreditToInvoicesDTO,
IApplyCreditToInvoicesCreatedPayload,
} from '@/interfaces';
import { ServiceError } from '@/exceptions';
import events from '@/subscribers/events';
import { ERRORS } from './constants';
import HasTenancyService from '../Tenancy/TenancyService';
@Service()
export default class CreditNoteApplyToInvoices extends BaseCreditNotes {
@Inject('PaymentReceives')
paymentReceive: PaymentReceiveService;
@Inject()
private tenancy: HasTenancyService;
@Inject()
uow: UnitOfWork;
private paymentReceiveValidators: PaymentReceiveValidators;
@Inject()
eventPublisher: EventPublisher;
private uow: UnitOfWork;
@Inject()
private eventPublisher: EventPublisher;
/**
* Apply credit note to the given invoices.
@@ -50,7 +54,7 @@ export default class CreditNoteApplyToInvoices extends BaseCreditNotes {
);
// Retrieve the applied invoices that associated to the credit note customer.
const appliedInvoicesEntries =
await this.paymentReceive.validateInvoicesIDsExistance(
await this.paymentReceiveValidators.validateInvoicesIDsExistance(
tenantId,
creditNote.customerId,
applyCreditToInvoicesDTO.entries

View File

@@ -15,7 +15,7 @@ export default class CreditNoteInventoryTransactionsSubscriber {
/**
* Attaches events with publisher.
*/
attach(bus) {
public attach(bus) {
bus.subscribe(
events.creditNote.onCreated,
this.writeInventoryTranscationsOnceCreated
@@ -37,6 +37,7 @@ export default class CreditNoteInventoryTransactionsSubscriber {
/**
* Writes inventory transactions once credit note created.
* @param {ICreditNoteCreatedPayload} payload -
* @returns {Promise<void>}
*/
public writeInventoryTranscationsOnceCreated = async ({
tenantId,
@@ -44,9 +45,8 @@ export default class CreditNoteInventoryTransactionsSubscriber {
trx,
}: ICreditNoteCreatedPayload) => {
// Can't continue if the credit note is open yet.
if (!creditNote.isOpen) {
return;
}
if (!creditNote.isOpen) return;
await this.inventoryTransactions.createInventoryTransactions(
tenantId,
creditNote,
@@ -57,6 +57,7 @@ export default class CreditNoteInventoryTransactionsSubscriber {
/**
* Rewrites inventory transactions once credit note edited.
* @param {ICreditNoteEditedPayload} payload -
* @returns {Promise<void>}
*/
public rewriteInventoryTransactionsOnceEdited = async ({
tenantId,
@@ -65,9 +66,8 @@ export default class CreditNoteInventoryTransactionsSubscriber {
trx,
}: ICreditNoteEditedPayload) => {
// Can't continue if the credit note is open yet.
if (!creditNote.isOpen) {
return;
}
if (!creditNote.isOpen) return;
await this.inventoryTransactions.editInventoryTransactions(
tenantId,
creditNoteId,
@@ -87,9 +87,8 @@ export default class CreditNoteInventoryTransactionsSubscriber {
trx,
}: ICreditNoteDeletedPayload) => {
// Can't continue if the credit note is open yet.
if (!oldCreditNote.isOpen) {
return;
}
if (!oldCreditNote.isOpen) return;
await this.inventoryTransactions.deleteInventoryTransactions(
tenantId,
creditNoteId,

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