feat(server): add bank card entity

This commit is contained in:
Sergey Krylov 2025-09-11 05:28:44 +03:00
parent 920daee26e
commit 0b6f2da0d3
21 changed files with 1003 additions and 266 deletions

740
server/package-lock.json generated
View File

@ -1979,14 +1979,14 @@
"license": "MIT"
},
"node_modules/@stylistic/eslint-plugin": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.2.0.tgz",
"integrity": "sha512-RCEdbREv9EBiToUBQTlRhVYKG093I6ZnnQ990j08eJ6uRZh71DXkOnoxtTLfDQ6utVCVQzrhZFHZP0zfrfOIjA==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.3.1.tgz",
"integrity": "sha512-Ykums1VYonM0TgkD0VteVq9mrlO2FhF48MDJnPyv3MktIB2ydtuhlO0AfWm7xnW1kyf5bjOqA6xc7JjviuVTxg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/types": "^8.37.0",
"@typescript-eslint/types": "^8.41.0",
"eslint-visitor-keys": "^4.2.1",
"espree": "^10.4.0",
"estraverse": "^5.3.0",
@ -1999,6 +1999,20 @@
"eslint": ">=9.0.0"
}
},
"node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/types": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.42.0.tgz",
"integrity": "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@stylistic/eslint-plugin/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
@ -2664,6 +2678,131 @@
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.42.0.tgz",
"integrity": "sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.42.0",
"@typescript-eslint/typescript-estree": "8.42.0",
"@typescript-eslint/utils": "8.42.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/project-service": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.42.0.tgz",
"integrity": "sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.42.0",
"@typescript-eslint/types": "^8.42.0",
"debug": "^4.3.4"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.42.0.tgz",
"integrity": "sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.42.0.tgz",
"integrity": "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.42.0.tgz",
"integrity": "sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.42.0",
"@typescript-eslint/tsconfig-utils": "8.42.0",
"@typescript-eslint/types": "8.42.0",
"@typescript-eslint/visitor-keys": "8.42.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
"minimatch": "^9.0.4",
"semver": "^7.6.0",
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.42.0.tgz",
"integrity": "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.42.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/types": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz",
@ -2708,16 +2847,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz",
"integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==",
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.42.0.tgz",
"integrity": "sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/scope-manager": "8.39.0",
"@typescript-eslint/types": "8.39.0",
"@typescript-eslint/typescript-estree": "8.39.0"
"@typescript-eslint/scope-manager": "8.42.0",
"@typescript-eslint/types": "8.42.0",
"@typescript-eslint/typescript-estree": "8.42.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -2731,6 +2870,124 @@
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/project-service": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.42.0.tgz",
"integrity": "sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.42.0",
"@typescript-eslint/types": "^8.42.0",
"debug": "^4.3.4"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.42.0.tgz",
"integrity": "sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.42.0",
"@typescript-eslint/visitor-keys": "8.42.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.42.0.tgz",
"integrity": "sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.42.0.tgz",
"integrity": "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.42.0.tgz",
"integrity": "sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.42.0",
"@typescript-eslint/tsconfig-utils": "8.42.0",
"@typescript-eslint/types": "8.42.0",
"@typescript-eslint/visitor-keys": "8.42.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
"minimatch": "^9.0.4",
"semver": "^7.6.0",
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.42.0.tgz",
"integrity": "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.42.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz",
@ -5320,56 +5577,6 @@
}
}
},
"node_modules/eslint-config-ksv741/node_modules/@typescript-eslint/scope-manager": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz",
"integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.37.0",
"@typescript-eslint/visitor-keys": "8.37.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/eslint-config-ksv741/node_modules/@typescript-eslint/types": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz",
"integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/eslint-config-ksv741/node_modules/@typescript-eslint/visitor-keys": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz",
"integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.37.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/eslint-config-ksv741/node_modules/globals": {
"version": "16.3.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz",
@ -5383,212 +5590,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint-config-ksv741/node_modules/ignore": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/eslint-config-ksv741/node_modules/typescript-eslint": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz",
"integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.37.0",
"@typescript-eslint/parser": "8.37.0",
"@typescript-eslint/typescript-estree": "8.37.0",
"@typescript-eslint/utils": "8.37.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/eslint-config-ksv741/node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz",
"integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.37.0",
"@typescript-eslint/type-utils": "8.37.0",
"@typescript-eslint/utils": "8.37.0",
"@typescript-eslint/visitor-keys": "8.37.0",
"graphemer": "^1.4.0",
"ignore": "^7.0.0",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.37.0",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/eslint-config-ksv741/node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz",
"integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.37.0",
"@typescript-eslint/typescript-estree": "8.37.0",
"@typescript-eslint/utils": "8.37.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/eslint-config-ksv741/node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz",
"integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.37.0",
"@typescript-eslint/types": "8.37.0",
"@typescript-eslint/typescript-estree": "8.37.0",
"@typescript-eslint/visitor-keys": "8.37.0",
"debug": "^4.3.4"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/eslint-config-ksv741/node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz",
"integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.37.0",
"@typescript-eslint/tsconfig-utils": "8.37.0",
"@typescript-eslint/types": "8.37.0",
"@typescript-eslint/visitor-keys": "8.37.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
"minimatch": "^9.0.4",
"semver": "^7.6.0",
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/eslint-config-ksv741/node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz",
"integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.37.0",
"@typescript-eslint/types": "^8.37.0",
"debug": "^4.3.4"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/eslint-config-ksv741/node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz",
"integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/eslint-config-ksv741/node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz",
"integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/scope-manager": "8.37.0",
"@typescript-eslint/types": "8.37.0",
"@typescript-eslint/typescript-estree": "8.37.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <5.9.0"
}
},
"node_modules/eslint-import-context": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.9.tgz",
@ -10961,6 +10962,213 @@
"node": ">=14.17"
}
},
"node_modules/typescript-eslint": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.42.0.tgz",
"integrity": "sha512-ozR/rQn+aQXQxh1YgbCzQWDFrsi9mcg+1PM3l/z5o1+20P7suOIaNg515bpr/OYt6FObz/NHcBstydDLHWeEKg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/eslint-plugin": "8.42.0",
"@typescript-eslint/parser": "8.42.0",
"@typescript-eslint/typescript-estree": "8.42.0",
"@typescript-eslint/utils": "8.42.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.42.0.tgz",
"integrity": "sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.42.0",
"@typescript-eslint/type-utils": "8.42.0",
"@typescript-eslint/utils": "8.42.0",
"@typescript-eslint/visitor-keys": "8.42.0",
"graphemer": "^1.4.0",
"ignore": "^7.0.0",
"natural-compare": "^1.4.0",
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"@typescript-eslint/parser": "^8.42.0",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.42.0.tgz",
"integrity": "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/scope-manager": "8.42.0",
"@typescript-eslint/types": "8.42.0",
"@typescript-eslint/typescript-estree": "8.42.0",
"@typescript-eslint/visitor-keys": "8.42.0",
"debug": "^4.3.4"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/project-service": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.42.0.tgz",
"integrity": "sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.42.0",
"@typescript-eslint/types": "^8.42.0",
"debug": "^4.3.4"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.42.0.tgz",
"integrity": "sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.42.0",
"@typescript-eslint/visitor-keys": "8.42.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.42.0.tgz",
"integrity": "sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/types": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.42.0.tgz",
"integrity": "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.42.0.tgz",
"integrity": "sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.42.0",
"@typescript-eslint/tsconfig-utils": "8.42.0",
"@typescript-eslint/types": "8.42.0",
"@typescript-eslint/visitor-keys": "8.42.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
"minimatch": "^9.0.4",
"semver": "^7.6.0",
"ts-api-utils": "^2.1.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": {
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.42.0.tgz",
"integrity": "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.42.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/typescript-eslint/node_modules/ignore": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/uid": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz",

View File

@ -53,5 +53,9 @@
"ts-node": "10.9.2",
"tsconfig-paths": "4.2.0",
"typescript": "5.9.2"
},
"overrides": {
"@stylistic/eslint-plugin": "5.3.1",
"typescript-eslint": "8.42.0"
}
}

View File

@ -26,6 +26,7 @@ model User {
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
expensesLists ExpenseList[]
BankCard BankCard[]
@@map("users")
}
@ -57,3 +58,26 @@ model ExpenseItem {
@@map("expenses_items")
}
model BankAccount {
id String @id @default(uuid())
balance Float @default(0)
cards BankCard[]
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("bank_accounts")
}
model BankCard {
id String @id @default(uuid())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
name String
bankAccount BankAccount @relation(fields: [bankAccountId], references: [id], onDelete: Cascade)
bankAccountId String
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("bank_cards")
}

View File

@ -2,6 +2,8 @@ import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AuthModule } from './auth/auth.module';
import { BankAccountModule } from './bank-account/bank-account.module';
import { BankCardModule } from './bank-card/bank-card.module';
import { ExpensesModule } from './expenses/expenses.module';
import { PrismaModule } from './prisma/prisma.module';
@ -13,6 +15,8 @@ import { PrismaModule } from './prisma/prisma.module';
PrismaModule,
AuthModule,
ExpensesModule,
BankAccountModule,
BankCardModule,
],
})
export class AppModule {}

View File

@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { BankAccountService } from './bank-account.service';
@Module({
providers: [BankAccountService],
exports: [BankAccountService],
})
export class BankAccountModule {}

View File

@ -0,0 +1,46 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { BankAccount } from '@prisma/client';
import { PrismaService } from '@/modules/prisma/prisma.service';
import { CreateBankAccountDto } from './dto';
@Injectable()
export class BankAccountService {
constructor(private readonly prismaService: PrismaService) {}
async createBankAccount(dto: CreateBankAccountDto) {
const { balance } = dto;
return this.prismaService.bankAccount.create({
data: {
balance,
},
});
}
async getBankAccountById(id: BankAccount['id']) {
const bankAccount = await this.prismaService.bankAccount.findUnique({ where: { id } });
if (!bankAccount) {
throw new NotFoundException('Счет не найден');
}
return bankAccount;
}
async getBalance(id: BankAccount['id']) {
const account = await this.getBankAccountById(id);
return account.balance;
}
async changeBalance(id: BankAccount['id'], amount: number) {
const account = await this.getBankAccountById(id);
return this.prismaService.bankAccount.update({
where: { id: account.id },
data: { balance: amount },
});
}
}

View File

@ -0,0 +1,16 @@
import { ApiProperty } from '@nestjs/swagger';
import {
IsNumber,
IsOptional,
} from 'class-validator';
export class CreateBankAccountDto {
@ApiProperty({
description: 'Баланс',
example: 100.95,
type: Number,
})
@IsNumber({ maxDecimalPlaces: 2 }, { message: 'Баланс должен быть числом' })
@IsOptional()
balance?: number;
}

View File

@ -0,0 +1 @@
export { CreateBankAccountDto } from './CreateBankAccountDto';

View File

@ -0,0 +1,70 @@
import {
Body,
Controller,
Delete,
Get,
Param,
Patch,
Post,
} from '@nestjs/common';
import { BankCard, User } from '@prisma/client';
import { CreateBankCardDto, EditBankCardDto } from '@/modules/bank-card/dto';
import { Authorized } from '@/shared/decorators';
import { BankCardService } from './bank-card.service';
import {
CreateBankCard,
DeleteBankCard,
EditBankCard,
GetAllBankCard,
GetBankCard,
} from './decorators';
@Controller('bank-card')
export class BankCardController {
constructor(private readonly bankCardService: BankCardService) {}
@CreateBankCard()
@Post()
async createBankCard(
@Authorized('userId') userId: User['id'],
@Body() dto: CreateBankCardDto,
) {
return this.bankCardService.createBankCard(userId, dto);
}
@EditBankCard()
@Patch(':id')
async editBankCard(
@Authorized('userId') userId: User['id'],
@Param('id') id: BankCard['id'],
@Body() dto: EditBankCardDto,
) {
return this.bankCardService.editBankCard(userId, id, dto);
}
@GetBankCard()
@Get(':id')
async getBankCardById(
@Authorized('userId') userId: User['id'],
@Param('id') id: BankCard['id'],
) {
return this.bankCardService.getBankCardById(userId, id);
}
@DeleteBankCard()
@Delete(':id')
async deleteBankCardById(
@Authorized('userId') userId: User['id'],
@Param('id') id: BankCard['id'],
) {
return this.bankCardService.deleteBankCard(userId, id);
}
@GetAllBankCard()
@Get()
async getBankCards(@Authorized('userId') userId: User['id']) {
return this.bankCardService.getBankCardsByUserId(userId);
}
}

View File

@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { BankAccountModule } from '@/modules/bank-account/bank-account.module';
import { BankCardController } from './bank-card.controller';
import { BankCardService } from './bank-card.service';
@Module({
controllers: [BankCardController],
providers: [BankCardService],
imports: [BankAccountModule],
})
export class BankCardModule {}

View File

@ -0,0 +1,102 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { BankAccount, BankCard, User } from '@prisma/client';
import { BankAccountService } from '@/modules/bank-account/bank-account.service';
import { PrismaService } from '@/modules/prisma/prisma.service';
import { BankCardDto, CreateBankCardDto, EditBankCardDto } from './dto';
@Injectable()
export class BankCardService {
constructor(
private readonly prismaService: PrismaService,
private readonly bankAccountService: BankAccountService,
) {}
async createBankCard(userId: User['id'], dto: CreateBankCardDto): Promise<BankCardDto> {
const { name, balance } = dto;
const bankAccount = await this.bankAccountService.createBankAccount({
balance,
});
const card = await this.prismaService.bankCard.create({
data: {
name,
user: {
connect: { id: userId },
},
bankAccount: {
connect: { id: bankAccount.id },
},
},
include: { bankAccount: true },
});
return this.toDto(card);
}
async getBankCardById(userId: User['id'], bankCardId: BankCard['id']): Promise<BankCardDto> {
const card = await this.prismaService.bankCard.findUnique({
where: { id: bankCardId },
include: { bankAccount: true },
});
if (card?.userId !== userId) {
throw new NotFoundException('Карта не найдена');
}
return this.toDto(card);
}
async getBankCardsByUserId(userId: User['id']): Promise<BankCardDto[]> {
const cards = await this.prismaService.bankCard.findMany({
where: { userId },
include: { bankAccount: true },
orderBy: { createdAt: 'asc' },
});
return cards.map((card) => this.toDto(card));
}
async editBankCard(userId: User['id'], bankCardId: BankCard['id'], dto: EditBankCardDto): Promise<BankCardDto> {
const { name, balance } = dto;
const card = await this.getBankCardById(userId, bankCardId);
const updatedCard = await this.prismaService.bankCard.update({
where: { id: card.id },
data: {
name: name ?? card.name,
bankAccount: {
update: {
balance: balance ?? card.balance,
},
},
},
include: { bankAccount: true },
});
return this.toDto(updatedCard);
}
async deleteBankCard(userId: User['id'], bankCardId: BankCard['id']): Promise<BankCardDto> {
const card = await this.getBankCardById(userId, bankCardId);
const deletedCard = await this.prismaService.bankCard.delete({
where: { id: card.id },
include: { bankAccount: true },
});
return this.toDto(deletedCard);
}
toDto(card: BankCard & { bankAccount: BankAccount }): BankCardDto {
return {
id: card.id,
name: card.name,
balance: card.bankAccount.balance,
createdAt: card.createdAt,
updatedAt: card.updatedAt,
};
}
}

View File

@ -0,0 +1,24 @@
import { applyDecorators } from '@nestjs/common';
import { ApiBearerAuth, ApiOkResponse, ApiOperation } from '@nestjs/swagger';
import { Authorization } from '@/shared/decorators';
import { BankCardDto } from '../dto';
function Documentation() {
return applyDecorators(
ApiOperation({
summary: 'Создать карту',
description: 'Создать банковскую карту',
}),
ApiBearerAuth(),
ApiOkResponse({ description: 'Успешно создана банковская карта', type: BankCardDto }),
);
}
export function CreateBankCard() {
return applyDecorators(
Authorization(),
Documentation(),
);
}

View File

@ -0,0 +1,32 @@
import { applyDecorators } from '@nestjs/common';
import {
ApiBearerAuth,
ApiNotFoundResponse,
ApiOkResponse,
ApiOperation,
ApiParam,
} from '@nestjs/swagger';
import { Authorization } from '@/shared/decorators';
import { BankCardDto } from '../dto';
function Documentation() {
return applyDecorators(
ApiOperation({
summary: 'Удалить карту',
description: 'Удалить банковскую карту',
}),
ApiBearerAuth(),
ApiParam({ name: 'id', type: String, description: 'ID карты' }),
ApiNotFoundResponse({ description: 'Карта не найдена' }),
ApiOkResponse({ description: 'Успешно удалена банковская карта', type: BankCardDto }),
);
}
export function DeleteBankCard() {
return applyDecorators(
Authorization(),
Documentation(),
);
}

View File

@ -0,0 +1,32 @@
import { applyDecorators } from '@nestjs/common';
import {
ApiBearerAuth,
ApiNotFoundResponse,
ApiOkResponse,
ApiOperation,
ApiParam,
} from '@nestjs/swagger';
import { Authorization } from '@/shared/decorators';
import { BankCardDto } from '../dto';
function Documentation() {
return applyDecorators(
ApiOperation({
summary: 'Редактировать карту',
description: 'Редактировать банковскую карту',
}),
ApiBearerAuth(),
ApiParam({ name: 'id', type: String, description: 'ID карты' }),
ApiNotFoundResponse({ description: 'Карта не найдена' }),
ApiOkResponse({ description: 'Успешно отредактирована банковская карта', type: BankCardDto }),
);
}
export function EditBankCard() {
return applyDecorators(
Authorization(),
Documentation(),
);
}

View File

@ -0,0 +1,24 @@
import { applyDecorators } from '@nestjs/common';
import { ApiBearerAuth, ApiOkResponse, ApiOperation } from '@nestjs/swagger';
import { Authorization } from '@/shared/decorators';
import { BankCardDto } from '../dto';
function Documentation() {
return applyDecorators(
ApiOperation({
summary: 'Найти все карты',
description: 'Найти все банковсие карты',
}),
ApiBearerAuth(),
ApiOkResponse({ description: 'Успешно найдены банковские карты', type: [BankCardDto] }),
);
}
export function GetAllBankCard() {
return applyDecorators(
Authorization(),
Documentation(),
);
}

View File

@ -0,0 +1,32 @@
import { applyDecorators } from '@nestjs/common';
import {
ApiBearerAuth,
ApiNotFoundResponse,
ApiOkResponse,
ApiOperation,
ApiParam,
} from '@nestjs/swagger';
import { Authorization } from '@/shared/decorators';
import { BankCardDto } from '../dto';
function Documentation() {
return applyDecorators(
ApiOperation({
summary: 'Найти карту',
description: 'Найти банковскую карту',
}),
ApiBearerAuth(),
ApiParam({ name: 'id', type: String, description: 'ID карты' }),
ApiNotFoundResponse({ description: 'Карта не найдена' }),
ApiOkResponse({ description: 'Успешно найдена банковская карта', type: BankCardDto }),
);
}
export function GetBankCard() {
return applyDecorators(
Authorization(),
Documentation(),
);
}

View File

@ -0,0 +1,5 @@
export { CreateBankCard } from './create-bank-card.decorator';
export { EditBankCard } from './edit-bank-card.decorator';
export { GetBankCard } from './get-bank-card.decorator';
export { DeleteBankCard } from './delete-bank-card.decorator';
export { GetAllBankCard } from './get-all-bank-card.decorator';

View File

@ -0,0 +1,35 @@
import { ApiProperty } from '@nestjs/swagger';
export class BankCardDto {
@ApiProperty({
description: 'ID',
type: String,
})
id: string;
@ApiProperty({
description: 'Название карты',
example: 'Карта Сбербанка',
type: String,
})
name: string;
@ApiProperty({
description: 'Баланс карты',
example: 100.95,
type: Number,
})
balance: number;
@ApiProperty({
description: 'Дата создания',
type: Date,
})
createdAt: Date;
@ApiProperty({
description: 'Дата обновления',
type: Date,
})
updatedAt: Date;
}

View File

@ -0,0 +1,27 @@
import { ApiProperty } from '@nestjs/swagger';
import {
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
export class CreateBankCardDto {
@ApiProperty({
description: 'Название карты',
example: 'Карта Сбербанка',
type: String,
})
@IsString({ message: 'Название карты должно быть строкой' })
@IsNotEmpty({ message: 'Название карты обязательно к заполнению' })
name: string;
@ApiProperty({
description: 'Баланс карты',
example: 100.95,
type: Number,
})
@IsNumber({ maxDecimalPlaces: 2 }, { message: 'Баланс карты должен быть числом' })
@IsOptional()
balance?: number;
}

View File

@ -0,0 +1,26 @@
import { ApiProperty } from '@nestjs/swagger';
import {
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
export class EditBankCardDto {
@ApiProperty({
description: 'Название карта',
example: 'Карта Сбербанка',
type: String,
})
@IsString({ message: 'Название карты должно быть строкой' })
@IsOptional()
name?: string;
@ApiProperty({
description: 'Баланс карты',
example: 100.95,
type: Number,
})
@IsNumber({ maxDecimalPlaces: 2 }, { message: 'Баланс карты должен быть числом' })
@IsOptional()
balance?: number;
}

View File

@ -0,0 +1,3 @@
export { CreateBankCardDto } from './create-bank-card.dto';
export { EditBankCardDto } from './edit-bank-card.dto';
export { BankCardDto } from './bank-card.dto';