@merkaly/api 0.2.4-5 → 0.2.4-7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/.DS_Store +0 -0
  2. package/.bin/deploy.sh +9 -0
  3. package/.bin/package.sh +7 -0
  4. package/.docker/Dockerfile +21 -0
  5. package/.dockerignore +12 -0
  6. package/.env +21 -0
  7. package/.eslintignore +8 -0
  8. package/.eslintrc.js +35 -0
  9. package/.github/dependabot.yml +17 -0
  10. package/.github/semantic.yml +5 -0
  11. package/.github/workflows/pull_request.yml +35 -0
  12. package/.gitignore +42 -0
  13. package/.husky/.gitignore +1 -0
  14. package/.husky/commit-msg +5 -0
  15. package/.husky/common.sh +8 -0
  16. package/.husky/pre-commit +5 -0
  17. package/.husky/pre-push +4 -0
  18. package/.idea/.gitignore +5 -0
  19. package/.idea/api.iml +13 -0
  20. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  21. package/.idea/inspectionProfiles/Project_Default.xml +11 -0
  22. package/.idea/modules.xml +8 -0
  23. package/.idea/vcs.xml +12 -0
  24. package/.idea/workspace.xml +2320 -0
  25. package/.output/modules/sales/orders/billing/billing.entity.d.ts +8 -6
  26. package/.output/modules/sales/orders/billing/billing.entity.js +14 -9
  27. package/.output/modules/sales/orders/billing/billing.types.d.ts +9 -0
  28. package/.output/modules/sales/orders/billing/billing.types.js +14 -0
  29. package/.output/modules/sales/orders/billing/billing.validator.d.ts +6 -10
  30. package/.output/modules/sales/orders/billing/billing.validator.js +10 -15
  31. package/.output/modules/sales/orders/{address → billing/entities}/address.entity.d.ts +2 -1
  32. package/.output/modules/sales/orders/{address → billing/entities}/address.entity.js +17 -17
  33. package/.output/modules/sales/orders/billing/entities/customer.entity.d.ts +5 -0
  34. package/.output/modules/sales/orders/billing/entities/customer.entity.js +52 -0
  35. package/.output/modules/sales/orders/billing/entities/status.entity.d.ts +6 -0
  36. package/.output/modules/sales/orders/billing/entities/status.entity.js +37 -0
  37. package/.output/modules/sales/orders/{address → billing/validators}/address.validator.d.ts +2 -1
  38. package/.output/modules/sales/orders/{address → billing/validators}/address.validator.js +12 -14
  39. package/.output/modules/sales/orders/billing/validators/customer.validator.d.ts +5 -0
  40. package/.output/modules/sales/orders/billing/validators/customer.validator.js +36 -0
  41. package/.output/modules/sales/orders/customer/customer.validator.d.ts +1 -1
  42. package/.output/modules/sales/orders/customer/customer.validator.js +8 -8
  43. package/.output/modules/sales/orders/order.entity.d.ts +1 -1
  44. package/.output/modules/sales/orders/order.validator.d.ts +2 -2
  45. package/.output/modules/sales/orders/order.validator.js +3 -4
  46. package/.output/modules/sales/orders/shipping/entities/address.entity.d.ts +10 -0
  47. package/.output/modules/sales/orders/shipping/entities/address.entity.js +68 -0
  48. package/.output/modules/sales/orders/shipping/entities/customer.entity.d.ts +5 -0
  49. package/.output/modules/sales/orders/shipping/entities/customer.entity.js +52 -0
  50. package/.output/modules/sales/orders/shipping/entities/status.entity.d.ts +6 -0
  51. package/.output/modules/sales/orders/shipping/entities/status.entity.js +37 -0
  52. package/.output/modules/sales/orders/shipping/shipping.entity.d.ts +8 -6
  53. package/.output/modules/sales/orders/shipping/shipping.entity.js +14 -9
  54. package/.output/modules/sales/orders/shipping/shipping.types.d.ts +11 -0
  55. package/.output/modules/sales/orders/shipping/shipping.types.js +16 -0
  56. package/.output/modules/sales/orders/shipping/shipping.validator.d.ts +6 -9
  57. package/.output/modules/sales/orders/shipping/shipping.validator.js +10 -14
  58. package/.output/modules/sales/orders/shipping/validators/address.validator.d.ts +10 -0
  59. package/.output/modules/sales/orders/shipping/validators/address.validator.js +56 -0
  60. package/.output/modules/sales/orders/shipping/validators/customer.validator.d.ts +5 -0
  61. package/.output/modules/sales/orders/shipping/validators/customer.validator.js +36 -0
  62. package/authCertificate.pem +19 -0
  63. package/cloudbuild.yaml +20 -0
  64. package/commitlint.config.js +3 -0
  65. package/jest.config.js +14 -0
  66. package/merkaly-api-0.2.4-6.tgz +0 -0
  67. package/nest-cli.json +9 -0
  68. package/package.json +8 -7
  69. package/serviceAccount.json +7 -0
  70. package/src/abstract/absctract.listener.ts +2 -0
  71. package/src/abstract/abstract.controller.ts +27 -0
  72. package/src/abstract/abstract.entity.ts +19 -0
  73. package/src/abstract/abstract.exception.ts +56 -0
  74. package/src/abstract/abstract.fixture.ts +7 -0
  75. package/src/abstract/abstract.migration.ts +13 -0
  76. package/src/abstract/abstract.repository.ts +79 -0
  77. package/src/abstract/abstract.router.ts +7 -0
  78. package/src/abstract/abstract.validator.ts +62 -0
  79. package/src/app.config.ts +57 -0
  80. package/src/app.console.ts +30 -0
  81. package/src/app.controller.ts +48 -0
  82. package/src/app.migration.ts +7 -0
  83. package/src/app.module.ts +92 -0
  84. package/src/app.strategy.ts +25 -0
  85. package/src/commands/assets.command.ts +38 -0
  86. package/src/commands/deploy.command.ts +6 -0
  87. package/src/commands/fixture.command.ts +11 -0
  88. package/src/commands/generate.command.ts +286 -0
  89. package/src/decorators/public.decorator.ts +5 -0
  90. package/src/decorators/user.decorator.ts +21 -0
  91. package/src/exceptions/missing-identity.exception.ts +11 -0
  92. package/src/exceptions/store-not-implemented.exception.ts +13 -0
  93. package/src/exceptions/store-not-recognized.exception.ts +11 -0
  94. package/src/guards/auth.guard.ts +43 -0
  95. package/src/guards/organization.guard.ts +39 -0
  96. package/src/interceptors/mongo.interceptor.ts +16 -0
  97. package/src/main.ts +37 -0
  98. package/src/middlewares/logger.middleware.ts +15 -0
  99. package/src/middlewares/organization.middleware.ts +76 -0
  100. package/src/migrations/1667226478717-product_price_enhance.ts +29 -0
  101. package/src/migrations/1667329249561-product_add_seo_code_dimension.ts +37 -0
  102. package/src/migrations/1667601099139-product_activve_and_hashtag.ts +35 -0
  103. package/src/migrations/1668136428972-content_banners-use-as-image.ts +56 -0
  104. package/src/migrations/1680483744321-order-billing-shipping-status.ts +32 -0
  105. package/src/modules/assets/asset.controller.ts +33 -0
  106. package/src/modules/assets/asset.entity.ts +26 -0
  107. package/src/modules/assets/asset.module.ts +21 -0
  108. package/src/modules/assets/asset.repository.ts +51 -0
  109. package/src/modules/assets/asset.schema.ts +4 -0
  110. package/src/modules/assets/asset.service.ts +41 -0
  111. package/src/modules/auth/auth.controller.ts +42 -0
  112. package/src/modules/auth/auth.module.ts +18 -0
  113. package/src/modules/auth/logout.ts +0 -0
  114. package/src/modules/command.module.ts +19 -0
  115. package/src/modules/content/banners/banner.controller.ts +56 -0
  116. package/src/modules/content/banners/banner.entity.ts +17 -0
  117. package/src/modules/content/banners/banner.fixture.ts +19 -0
  118. package/src/modules/content/banners/banner.listener.ts +6 -0
  119. package/src/modules/content/banners/banner.module.ts +21 -0
  120. package/src/modules/content/banners/banner.repository.ts +38 -0
  121. package/src/modules/content/banners/banner.schema.ts +4 -0
  122. package/src/modules/content/banners/banner.types.ts +7 -0
  123. package/src/modules/content/banners/banner.validator.ts +19 -0
  124. package/src/modules/content/content.module.ts +20 -0
  125. package/src/modules/content/pages/page.controller.ts +53 -0
  126. package/src/modules/content/pages/page.entity.ts +27 -0
  127. package/src/modules/content/pages/page.fixture.ts +19 -0
  128. package/src/modules/content/pages/page.listener.ts +6 -0
  129. package/src/modules/content/pages/page.module.ts +21 -0
  130. package/src/modules/content/pages/page.repository.ts +44 -0
  131. package/src/modules/content/pages/page.schema.ts +4 -0
  132. package/src/modules/content/pages/page.types.ts +9 -0
  133. package/src/modules/content/pages/page.validator.ts +29 -0
  134. package/src/modules/global.module.ts +70 -0
  135. package/src/modules/insight/controllers/address.controller.ts +29 -0
  136. package/src/modules/insight/controllers/order.controller.ts +70 -0
  137. package/src/modules/insight/controllers/products.controller.ts +18 -0
  138. package/src/modules/insight/insight.module.ts +24 -0
  139. package/src/modules/insight/validators/order.validator.ts +10 -0
  140. package/src/modules/inventory/brands/brand.controller.ts +59 -0
  141. package/src/modules/inventory/brands/brand.entity.ts +14 -0
  142. package/src/modules/inventory/brands/brand.exception.ts +9 -0
  143. package/src/modules/inventory/brands/brand.listener.ts +18 -0
  144. package/src/modules/inventory/brands/brand.module.ts +19 -0
  145. package/src/modules/inventory/brands/brand.repository.ts +39 -0
  146. package/src/modules/inventory/brands/brand.schema.ts +4 -0
  147. package/src/modules/inventory/brands/brand.validator.ts +23 -0
  148. package/src/modules/inventory/categories/category.controller.ts +59 -0
  149. package/src/modules/inventory/categories/category.entity.ts +21 -0
  150. package/src/modules/inventory/categories/category.exception.ts +9 -0
  151. package/src/modules/inventory/categories/category.fixture.ts +18 -0
  152. package/src/modules/inventory/categories/category.listener.ts +17 -0
  153. package/src/modules/inventory/categories/category.module.ts +19 -0
  154. package/src/modules/inventory/categories/category.repository.ts +40 -0
  155. package/src/modules/inventory/categories/category.schema.ts +4 -0
  156. package/src/modules/inventory/categories/category.validator.ts +30 -0
  157. package/src/modules/inventory/inventory.module.ts +22 -0
  158. package/src/modules/inventory/products/entities/code.entity.ts +15 -0
  159. package/src/modules/inventory/products/entities/dimension.entity.ts +18 -0
  160. package/src/modules/inventory/products/entities/price.entity.ts +12 -0
  161. package/src/modules/inventory/products/entities/seo.entity.ts +15 -0
  162. package/src/modules/inventory/products/product.controller.ts +59 -0
  163. package/src/modules/inventory/products/product.entity.ts +53 -0
  164. package/src/modules/inventory/products/product.exception.ts +9 -0
  165. package/src/modules/inventory/products/product.fixture.ts +19 -0
  166. package/src/modules/inventory/products/product.listener.ts +27 -0
  167. package/src/modules/inventory/products/product.module.ts +20 -0
  168. package/src/modules/inventory/products/product.repository.ts +108 -0
  169. package/src/modules/inventory/products/product.schema.ts +15 -0
  170. package/src/modules/inventory/products/product.validator.ts +71 -0
  171. package/src/modules/inventory/products/validators/code.validator.ts +15 -0
  172. package/src/modules/inventory/products/validators/dimension.validator.ts +19 -0
  173. package/src/modules/inventory/products/validators/price.validator.ts +10 -0
  174. package/src/modules/inventory/products/validators/seo.validator.ts +15 -0
  175. package/src/modules/inventory/properties/property.controller.ts +56 -0
  176. package/src/modules/inventory/properties/property.entity.ts +18 -0
  177. package/src/modules/inventory/properties/property.exception.ts +9 -0
  178. package/src/modules/inventory/properties/property.listener.ts +20 -0
  179. package/src/modules/inventory/properties/property.module.ts +21 -0
  180. package/src/modules/inventory/properties/property.repository.ts +23 -0
  181. package/src/modules/inventory/properties/property.schema.ts +4 -0
  182. package/src/modules/inventory/properties/property.validator.ts +27 -0
  183. package/src/modules/sales/clients/client.controller.ts +60 -0
  184. package/src/modules/sales/clients/client.entity.ts +25 -0
  185. package/src/modules/sales/clients/client.exception.ts +9 -0
  186. package/src/modules/sales/clients/client.listener.ts +20 -0
  187. package/src/modules/sales/clients/client.module.ts +19 -0
  188. package/src/modules/sales/clients/client.repository.ts +37 -0
  189. package/src/modules/sales/clients/client.schema.ts +4 -0
  190. package/src/modules/sales/clients/client.validator.ts +43 -0
  191. package/src/modules/sales/orders/billing/billing.entity.ts +22 -0
  192. package/src/modules/sales/orders/billing/billing.types.ts +10 -0
  193. package/src/modules/sales/orders/billing/billing.validator.ts +21 -0
  194. package/src/modules/sales/orders/billing/entities/address.entity.ts +27 -0
  195. package/src/modules/sales/orders/billing/entities/customer.entity.ts +14 -0
  196. package/src/modules/sales/orders/billing/entities/status.entity.ts +16 -0
  197. package/src/modules/sales/orders/billing/validators/address.validator.ts +28 -0
  198. package/src/modules/sales/orders/billing/validators/customer.validator.ts +14 -0
  199. package/src/modules/sales/orders/customer/customer.entity.ts +14 -0
  200. package/src/modules/sales/orders/customer/customer.validator.ts +14 -0
  201. package/src/modules/sales/orders/item/item.entity.ts +21 -0
  202. package/src/modules/sales/orders/item/item.schema.ts +8 -0
  203. package/src/modules/sales/orders/item/item.validator.ts +10 -0
  204. package/src/modules/sales/orders/order.controller.ts +93 -0
  205. package/src/modules/sales/orders/order.entity.ts +47 -0
  206. package/src/modules/sales/orders/order.listener.ts +20 -0
  207. package/src/modules/sales/orders/order.module.ts +19 -0
  208. package/src/modules/sales/orders/order.repository.ts +189 -0
  209. package/src/modules/sales/orders/order.schema.ts +35 -0
  210. package/src/modules/sales/orders/order.validator.ts +72 -0
  211. package/src/modules/sales/orders/shipping/entities/address.entity.ts +27 -0
  212. package/src/modules/sales/orders/shipping/entities/customer.entity.ts +14 -0
  213. package/src/modules/sales/orders/shipping/entities/status.entity.ts +16 -0
  214. package/src/modules/sales/orders/shipping/shipping.entity.ts +25 -0
  215. package/src/modules/sales/orders/shipping/shipping.types.ts +12 -0
  216. package/src/modules/sales/orders/shipping/shipping.validator.ts +25 -0
  217. package/src/modules/sales/orders/shipping/validators/address.validator.ts +28 -0
  218. package/src/modules/sales/orders/shipping/validators/customer.validator.ts +14 -0
  219. package/src/modules/sales/orders/status/status.entity.ts +16 -0
  220. package/src/modules/sales/orders/status/status.validator.ts +8 -0
  221. package/src/modules/sales/sales.module.ts +20 -0
  222. package/src/modules/setting/connections/connection.controller.ts +27 -0
  223. package/src/modules/setting/connections/connection.module.ts +17 -0
  224. package/src/modules/setting/connections/connection.validator.ts +15 -0
  225. package/src/modules/setting/layout/layout.controller.ts +34 -0
  226. package/src/modules/setting/layout/layout.entity.ts +13 -0
  227. package/src/modules/setting/layout/layout.listener.ts +7 -0
  228. package/src/modules/setting/layout/layout.module.ts +21 -0
  229. package/src/modules/setting/layout/layout.repository.ts +29 -0
  230. package/src/modules/setting/layout/layout.schema.ts +4 -0
  231. package/src/modules/setting/members/member.controller.ts +22 -0
  232. package/src/modules/setting/members/member.module.ts +17 -0
  233. package/src/modules/setting/members/member.validator.ts +3 -0
  234. package/src/modules/setting/organization/organization.controller.ts +33 -0
  235. package/src/modules/setting/organization/organization.entity.ts +27 -0
  236. package/src/modules/setting/organization/organization.listener.ts +46 -0
  237. package/src/modules/setting/organization/organization.module.ts +21 -0
  238. package/src/modules/setting/organization/organization.repository.ts +55 -0
  239. package/src/modules/setting/organization/organization.types.ts +65 -0
  240. package/src/modules/setting/organization/organization.validator.ts +56 -0
  241. package/src/modules/setting/setting.module.ts +23 -0
  242. package/src/modules/setting/theme/theme.controller.ts +34 -0
  243. package/src/modules/setting/theme/theme.entity.ts +13 -0
  244. package/src/modules/setting/theme/theme.listener.ts +6 -0
  245. package/src/modules/setting/theme/theme.module.ts +21 -0
  246. package/src/modules/setting/theme/theme.repository.ts +23 -0
  247. package/src/modules/setting/theme/theme.schema.ts +4 -0
  248. package/src/modules/users/user.controller.ts +75 -0
  249. package/src/modules/users/user.module.ts +20 -0
  250. package/src/modules/users/user.validator.ts +28 -0
  251. package/src/providers/auth0.provider.ts +36 -0
  252. package/src/providers/storage.provider.ts +22 -0
  253. package/src/services/auth0.service.ts +7 -0
  254. package/src/services/logger.service.ts +33 -0
  255. package/src/services/mongo.service.ts +44 -0
  256. package/src/services/storage.service.ts +39 -0
  257. package/src/types.ts +19 -0
  258. package/tsconfig.json +40 -0
  259. package/tsconfig.package.json +20 -0
  260. package/yarn.lock +8782 -0
@@ -0,0 +1,21 @@
1
+ import { Prop, Schema } from '@nestjs/mongoose';
2
+ import { Schema as MongoSchema, Types } from 'mongoose';
3
+ import { AbstractEntity } from '../../../abstract/abstract.entity';
4
+ import { PropertyEntity } from '../properties/property.entity';
5
+
6
+ @Schema({ timestamps: true })
7
+ export class CategoryEntity extends AbstractEntity {
8
+ public static readonly $index = 'inventory_categories';
9
+
10
+ @Prop({ type: MongoSchema.Types.String, length: 32, unique: true, required: true })
11
+ public name: string;
12
+
13
+ @Prop({ type: MongoSchema.Types.String })
14
+ public description: string;
15
+
16
+ @Prop({ type: MongoSchema.Types.String, length: 32, default: 'tag' })
17
+ public icon: string;
18
+
19
+ @Prop({ type: [{ type: Types.ObjectId, ref: PropertyEntity.$index }] })
20
+ public properties: PropertyEntity[] = [];
21
+ }
@@ -0,0 +1,9 @@
1
+ import { Catch, HttpException } from '@nestjs/common';
2
+ import { AbstractException } from '../../../abstract/abstract.exception';
3
+
4
+ @Catch()
5
+ export class CategoryException extends AbstractException {
6
+ public handleError(exception: HttpException) {
7
+ return exception;
8
+ }
9
+ }
@@ -0,0 +1,18 @@
1
+ import { AbstractFixture } from '../../../abstract/abstract.fixture';
2
+ import { CategoryEntity } from "./category.entity";
3
+
4
+ export class CategoryFixture extends AbstractFixture {
5
+
6
+ public normal() {
7
+ const category = new CategoryEntity({
8
+ _id: this.$faker.datatype.uuid(),
9
+ createdAt: this.$faker.date.past(1),
10
+ updatedAt: this.$faker.date.past(1),
11
+ });
12
+
13
+ category.name = this.$faker.vehicle.manufacturer();
14
+ category.description = this.$faker.lorem.sentence();
15
+
16
+ return category;
17
+ }
18
+ }
@@ -0,0 +1,17 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { AbsctractListener } from '../../../abstract/absctract.listener';
3
+
4
+ export enum CategoryEvent {
5
+ PRE_CREATE = 'database.inventory.category.create.pre',
6
+ POST_CREATE = 'database.inventory.category.create.post',
7
+
8
+ PRE_UPDATE = 'database.inventory.category.update.pre',
9
+ POST_UPDATE = 'database.inventory.category.update.post',
10
+
11
+ PRE_DELETE = 'database.inventory.category.delete.pre',
12
+ POST_DELETE = 'database.inventory.category.delete.post',
13
+ }
14
+
15
+ @Injectable()
16
+ export class CategoryListener extends AbsctractListener {
17
+ }
@@ -0,0 +1,19 @@
1
+ import { Module, ModuleMetadata } from '@nestjs/common';
2
+ import { AbstractRouter } from '../../../abstract/abstract.router';
3
+ import { CategoryController } from './category.controller';
4
+ import { CategoryListener } from './category.listener';
5
+ import { CategoryRepository } from './category.repository';
6
+
7
+ export const metadata: ModuleMetadata = {
8
+ imports: [],
9
+ controllers: [CategoryController],
10
+ providers: [CategoryRepository, CategoryListener],
11
+ exports: [CategoryRepository],
12
+ };
13
+
14
+ @Module(metadata)
15
+ export class CategoryModule extends AbstractRouter {
16
+ public static readonly path = '/categories';
17
+
18
+ public static readonly module = CategoryModule;
19
+ }
@@ -0,0 +1,40 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { InjectModel } from '@nestjs/mongoose';
3
+ import { Model, UpdateQuery } from 'mongoose';
4
+ import { AbstractRepository } from '../../../abstract/abstract.repository';
5
+ import { CategoryEntity } from './category.entity';
6
+ import { CreateCategoryValidator, UpdateCategoryValidator } from './category.validator';
7
+
8
+ @Injectable()
9
+ export class CategoryRepository extends AbstractRepository<CategoryEntity> {
10
+ @InjectModel(CategoryEntity.$index)
11
+ public readonly $model: Model<CategoryEntity>;
12
+
13
+ public async create(validator: CreateCategoryValidator) {
14
+ const category = new this.$model();
15
+
16
+ category.name = validator.name;
17
+ category.description = validator.description;
18
+ category.icon = validator.icon;
19
+
20
+ return super.$create(category);
21
+ }
22
+
23
+ public async update(id: string, validator: UpdateCategoryValidator) {
24
+ const category = await this.$model.findById(id);
25
+
26
+ const query: UpdateQuery<CategoryEntity> = {
27
+ $set: {
28
+ name: validator.name || category.name,
29
+ icon: validator.icon,
30
+ description: validator.description || category.description,
31
+ },
32
+ };
33
+
34
+ return super.$update(id, query);
35
+ }
36
+
37
+ public async delete(id: string): Promise<void> {
38
+ return super.$delete(id);
39
+ }
40
+ }
@@ -0,0 +1,4 @@
1
+ import { SchemaFactory } from '@nestjs/mongoose';
2
+ import { CategoryEntity } from './category.entity';
3
+
4
+ export const CategorySchema = SchemaFactory.createForClass(CategoryEntity);
@@ -0,0 +1,30 @@
1
+ import { IsOptional, IsString, Length } from 'class-validator';
2
+ import { AbstractValidator } from "../../../abstract/abstract.validator";
3
+
4
+ export class CreateCategoryValidator extends AbstractValidator {
5
+ @IsString()
6
+ @Length(5, 32)
7
+ public name: string;
8
+
9
+ @IsString()
10
+ @IsOptional()
11
+ public description?: string;
12
+
13
+ @IsString()
14
+ @IsOptional()
15
+ public icon = 'tag';
16
+ }
17
+
18
+ export class UpdateCategoryValidator extends AbstractValidator {
19
+ @IsString()
20
+ @IsOptional()
21
+ public name: string;
22
+
23
+ @IsString()
24
+ @IsOptional()
25
+ public description?: string;
26
+
27
+ @IsString()
28
+ @IsOptional()
29
+ public icon = 'tag';
30
+ }
@@ -0,0 +1,22 @@
1
+ import { Module, ModuleMetadata } from '@nestjs/common';
2
+ import { AbstractRouter } from '../../abstract/abstract.router';
3
+ import { BrandModule } from './brands/brand.module';
4
+ import { CategoryModule } from './categories/category.module';
5
+ import { ProductModule } from './products/product.module';
6
+ import { PropertyModule } from './properties/property.module';
7
+
8
+ export const metadata: ModuleMetadata = {
9
+ imports: [BrandModule, CategoryModule, ProductModule, PropertyModule],
10
+ controllers: [],
11
+ providers: [],
12
+ exports: [BrandModule, CategoryModule, ProductModule, PropertyModule],
13
+ };
14
+
15
+ @Module(metadata)
16
+ export class InventoryModule extends AbstractRouter {
17
+ public static readonly path = '/inventory';
18
+
19
+ public static readonly module = InventoryModule;
20
+
21
+ public static readonly children = metadata.imports;
22
+ }
@@ -0,0 +1,15 @@
1
+ import { Prop } from '@nestjs/mongoose';
2
+ import { Schema as Collection } from '@nestjs/mongoose/dist/decorators/schema.decorator';
3
+ import { Schema } from 'mongoose';
4
+
5
+ @Collection()
6
+ export class CodeEntity extends Map {
7
+ @Prop({ type: Schema.Types.String })
8
+ public sku: string;
9
+
10
+ @Prop({ type: Schema.Types.String })
11
+ public gtin: string;
12
+
13
+ @Prop({ type: Schema.Types.String })
14
+ public mpn: string;
15
+ }
@@ -0,0 +1,18 @@
1
+ import { Prop } from '@nestjs/mongoose';
2
+ import { Schema as Collection } from '@nestjs/mongoose/dist/decorators/schema.decorator';
3
+ import { Schema } from 'mongoose';
4
+
5
+ @Collection()
6
+ export class DimensionEntity extends Map {
7
+ @Prop({ type: Schema.Types.Number })
8
+ public weight: number;
9
+
10
+ @Prop({ type: Schema.Types.Number })
11
+ public height: number;
12
+
13
+ @Prop({ type: Schema.Types.Number })
14
+ public width: number;
15
+
16
+ @Prop({ type: Schema.Types.Number })
17
+ public depth: number;
18
+ }
@@ -0,0 +1,12 @@
1
+ import { Prop } from '@nestjs/mongoose';
2
+ import { Schema as Collection } from '@nestjs/mongoose/dist/decorators/schema.decorator';
3
+ import { Schema } from 'mongoose';
4
+
5
+ @Collection()
6
+ export class PriceEntity extends Map {
7
+ @Prop({ type: Schema.Types.Number, required: true })
8
+ public sale: number;
9
+
10
+ @Prop({ type: Schema.Types.Number })
11
+ public purchase: number;
12
+ }
@@ -0,0 +1,15 @@
1
+ import { Prop } from '@nestjs/mongoose';
2
+ import { Schema as Collection } from '@nestjs/mongoose/dist/decorators/schema.decorator';
3
+ import { Schema } from 'mongoose';
4
+
5
+ @Collection()
6
+ export class SeoEntity extends Map {
7
+ @Prop({ type: Schema.Types.String })
8
+ public title: string;
9
+
10
+ @Prop({ type: Schema.Types.String })
11
+ public slug: string;
12
+
13
+ @Prop({ type: Schema.Types.String })
14
+ public description: string;
15
+ }
@@ -0,0 +1,59 @@
1
+ import {
2
+ Body,
3
+ Controller,
4
+ Delete,
5
+ Get,
6
+ HttpCode,
7
+ HttpStatus,
8
+ Inject,
9
+ Param,
10
+ Patch,
11
+ Post,
12
+ Query,
13
+ UseFilters,
14
+ } from '@nestjs/common';
15
+ import { ApiTags } from '@nestjs/swagger';
16
+ import { AbstractController } from '../../../abstract/abstract.controller';
17
+ import { FindValidator } from '../../../abstract/abstract.validator';
18
+ import { ProductEntity } from './product.entity';
19
+ import { ProductException } from './product.exception';
20
+ import { ProductRepository } from './product.repository';
21
+ import { CreateProductValidator, UpdateProductValidator } from './product.validator';
22
+ import { Public } from "../../../decorators/public.decorator";
23
+
24
+ @Controller()
25
+ @UseFilters(ProductException)
26
+ @ApiTags('inventory')
27
+ export class ProductController extends AbstractController<ProductEntity> {
28
+ @Inject()
29
+ protected readonly $repository: ProductRepository;
30
+
31
+ @Get('/')
32
+ @Public()
33
+ public async find(@Query() validator: FindValidator<ProductEntity>) {
34
+ return super.find(validator);
35
+ }
36
+
37
+ @Post('/')
38
+ @HttpCode(HttpStatus.CREATED)
39
+ public async create(@Body() validator: CreateProductValidator) {
40
+ return this.$repository.create(validator);
41
+ }
42
+
43
+ @Get('/:id')
44
+ @Public()
45
+ public async read(@Param('id') id: string, @Query() validator: FindValidator<ProductEntity>) {
46
+ return this.$repository.read(id, validator);
47
+ }
48
+
49
+ @Patch('/:id')
50
+ public async update(@Param('id') id: string, @Body() validator: UpdateProductValidator) {
51
+ return this.$repository.update(id, validator);
52
+ }
53
+
54
+ @Delete('/:id')
55
+ @HttpCode(HttpStatus.NO_CONTENT)
56
+ public async delete(@Param('id') id: string) {
57
+ return this.$repository.delete(id);
58
+ }
59
+ }
@@ -0,0 +1,53 @@
1
+ import { Prop, Schema as Collection } from '@nestjs/mongoose';
2
+ import { Schema } from 'mongoose';
3
+ import { AbstractEntity } from '../../../abstract/abstract.entity';
4
+ import { AssetEntity } from '../../assets/asset.entity';
5
+ import { BrandEntity } from '../brands/brand.entity';
6
+ import { CategoryEntity } from '../categories/category.entity';
7
+ import { CodeEntity } from './entities/code.entity';
8
+ import { DimensionEntity } from './entities/dimension.entity';
9
+ import { PriceEntity } from './entities/price.entity';
10
+ import { SeoEntity } from './entities/seo.entity';
11
+
12
+ @Collection({ timestamps: true, toJSON: { virtuals: true } })
13
+ export class ProductEntity extends AbstractEntity {
14
+ public static readonly $index = 'inventory_products';
15
+
16
+ @Prop({ type: Schema.Types.String, required: true, length: 64 })
17
+ public name: string;
18
+
19
+ @Prop({ type: Schema.Types.String, length: 256 })
20
+ public measurement: string;
21
+
22
+ @Prop({ type: Schema.Types.Boolean, default: true })
23
+ public active = true;
24
+
25
+ @Prop({ type: Schema.Types.String, nullable: true })
26
+ public description: string;
27
+
28
+ @Prop({ type: Schema.Types.Array, default: [] })
29
+ public hashtags: string[] = [];
30
+
31
+ @Prop({ type: Schema.Types.ObjectId, ref: CategoryEntity.$index })
32
+ public category?: CategoryEntity;
33
+
34
+ @Prop({ type: Schema.Types.ObjectId, ref: BrandEntity.$index })
35
+ public brand?: BrandEntity;
36
+
37
+ @Prop({ type: [{ type: Schema.Types.ObjectId, ref: AssetEntity.$index }] })
38
+ public files: AssetEntity[] = [];
39
+
40
+ @Prop({ type: Schema.Types.Map, ref: () => PriceEntity, default: new Map() })
41
+ public price: PriceEntity;
42
+
43
+ @Prop({ type: Schema.Types.Map, ref: () => CodeEntity, default: new Map() })
44
+ public code: CodeEntity;
45
+
46
+ @Prop({ type: Schema.Types.Map, ref: () => DimensionEntity, default: new Map() })
47
+ public dimension: DimensionEntity;
48
+
49
+ @Prop({ type: Schema.Types.Map, ref: () => SeoEntity, default: new Map() })
50
+ public seo: SeoEntity;
51
+
52
+ public readonly picture: string
53
+ }
@@ -0,0 +1,9 @@
1
+ import { Catch, HttpException } from '@nestjs/common';
2
+ import { AbstractException } from '../../../abstract/abstract.exception';
3
+
4
+ @Catch()
5
+ export class ProductException extends AbstractException {
6
+ public handleError(exception: HttpException) {
7
+ return exception;
8
+ }
9
+ }
@@ -0,0 +1,19 @@
1
+ import { AbstractFixture } from '../../../abstract/abstract.fixture';
2
+ import { ProductEntity } from "./product.entity";
3
+
4
+ export class ProductFixture extends AbstractFixture {
5
+
6
+ public normal() {
7
+ const product = new ProductEntity({
8
+ _id: this.$faker.datatype.uuid(),
9
+ createdAt: this.$faker.date.past(1),
10
+ updatedAt: this.$faker.date.past(1),
11
+ });
12
+
13
+ product.name = this.$faker.vehicle.vehicle();
14
+ product.description = this.$faker.commerce.productDescription();
15
+ product.price.sale = Number(this.$faker.commerce.price(1000, 10000, 2));
16
+
17
+ return product;
18
+ }
19
+ }
@@ -0,0 +1,27 @@
1
+ import { Inject, Injectable } from '@nestjs/common';
2
+ import { OnEvent } from '@nestjs/event-emitter';
3
+ import { AbsctractListener } from '../../../abstract/absctract.listener';
4
+ import { AssetService } from '../../assets/asset.service';
5
+ import { ProductEntity } from './product.entity';
6
+
7
+ export enum ProductEvent {
8
+ LINK_FILES = 'database.inventory.product.link-files',
9
+ }
10
+
11
+ @Injectable()
12
+ export class ProductListener extends AbsctractListener {
13
+
14
+ @Inject()
15
+ protected $assets: AssetService;
16
+
17
+ @OnEvent(ProductEvent.LINK_FILES)
18
+ protected async linkAssets(product: ProductEntity) {
19
+ await product.populate('files');
20
+
21
+ const { _id, db: { name } } = product;
22
+
23
+ await this.$assets.removeAssetsWeakness(product.files, `${name}/products/${_id}`);
24
+
25
+ return product;
26
+ }
27
+ }
@@ -0,0 +1,20 @@
1
+ import { Module, ModuleMetadata } from '@nestjs/common';
2
+ import { AbstractRouter } from '../../../abstract/abstract.router';
3
+ import { AssetService } from '../../assets/asset.service';
4
+ import { ProductController } from './product.controller';
5
+ import { ProductListener } from './product.listener';
6
+ import { ProductRepository } from './product.repository';
7
+
8
+ export const metadata: ModuleMetadata = {
9
+ imports: [],
10
+ controllers: [ProductController],
11
+ providers: [ProductRepository, ProductListener, AssetService],
12
+ exports: [ProductRepository],
13
+ };
14
+
15
+ @Module(metadata)
16
+ export class ProductModule extends AbstractRouter {
17
+ public static readonly path = '/products';
18
+
19
+ public static readonly module = ProductModule;
20
+ }
@@ -0,0 +1,108 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { InjectModel } from '@nestjs/mongoose';
3
+ import { Model, Types } from 'mongoose';
4
+ import { AbstractRepository } from '../../../abstract/abstract.repository';
5
+ import { AssetEntity } from '../../assets/asset.entity';
6
+ import { BrandEntity } from '../brands/brand.entity';
7
+ import { CategoryEntity } from '../categories/category.entity';
8
+ import { ProductEntity } from './product.entity';
9
+ import { ProductEvent } from './product.listener';
10
+ import { CreateProductValidator, UpdateProductValidator } from './product.validator';
11
+ import { FindValidator } from "../../../abstract/abstract.validator";
12
+
13
+ @Injectable()
14
+ export class ProductRepository extends AbstractRepository<ProductEntity> {
15
+ @InjectModel(ProductEntity.$index)
16
+ public readonly $model: Model<ProductEntity>;
17
+
18
+ @InjectModel(AssetEntity.$index)
19
+ public readonly $asset: Model<AssetEntity>;
20
+
21
+ @InjectModel(BrandEntity.$index)
22
+ public readonly $brand: Model<BrandEntity>;
23
+
24
+ @InjectModel(CategoryEntity.$index)
25
+ public readonly $category: Model<CategoryEntity>;
26
+
27
+ public async create(validator: CreateProductValidator) {
28
+ const product: ProductEntity = new this.$model();
29
+
30
+ product.name = validator.name;
31
+ product.measurement = validator.measurement;
32
+ product.description = validator.description;
33
+ product.hashtags = validator.hashtags;
34
+ product.active = validator.active;
35
+ product.files = validator.files.map<AssetEntity>(file => new Types.ObjectId(file) as any);
36
+
37
+ product.brand = (validator.brand ? new Types.ObjectId(validator.brand) : product.brand) as any;
38
+ product.category = (validator.category ? new Types.ObjectId(validator.category) : product.category) as any;
39
+
40
+ product.price.set('sale', validator.price.sale);
41
+ product.price.set('purchase', validator.price.purchase);
42
+
43
+ product.seo.set('slug', validator.seo.slug);
44
+ product.seo.set('title', validator.seo.title);
45
+ product.seo.set('description', validator.seo.description);
46
+
47
+ product.code.set('gtin', validator.code.gtin);
48
+ product.code.set('sku', validator.code.sku);
49
+ product.code.set('mpn', validator.code.mpn);
50
+
51
+ product.dimension.set('depth', validator.dimension.depth);
52
+ product.dimension.set('height', validator.dimension.height);
53
+ product.dimension.set('weight', validator.dimension.weight);
54
+ product.dimension.set('width', validator.dimension.width);
55
+
56
+ await this.$model.create(product);
57
+
58
+ await this.$event.emitAsync(ProductEvent.LINK_FILES, product);
59
+
60
+ return product;
61
+ }
62
+
63
+ public async read(id: string, params: FindValidator<ProductEntity>): Promise<ProductEntity> {
64
+ return this.$model
65
+ .findOne({ [Types.ObjectId.isValid(id) ? '_id' : 'seo.slug']: id })
66
+ .populate(params.join)
67
+ }
68
+
69
+ public async update(id: string, validator: UpdateProductValidator) {
70
+ const product: ProductEntity = await this.$model.findById(id);
71
+
72
+ product.name = validator.name || product.name;
73
+ product.measurement = validator.measurement || product.measurement;
74
+ product.description = validator.description || product.description;
75
+ product.hashtags = validator.hashtags ?? product.hashtags;
76
+ product.active = validator.active ?? product.active;
77
+ product.files = validator.files.map<AssetEntity>(file => new Types.ObjectId(file) as any);
78
+
79
+ product.brand = (validator.brand ? new Types.ObjectId(validator.brand) : product.brand) as any;
80
+ product.category = (validator.category ? new Types.ObjectId(validator.category) : product.category) as any;
81
+
82
+ product.price.set('sale', validator.price.sale || product.price.get('sale'));
83
+ product.price.set('purchase', validator.price.purchase || product.price.get('purchase'));
84
+
85
+ product.seo.set('slug', validator.seo.slug ?? product.seo.slug);
86
+ product.seo.set('title', validator.seo.title ?? product.seo.title);
87
+ product.seo.set('description', validator.seo.description ?? product.seo.description);
88
+
89
+ product.code.set('gtin', validator.code.gtin ?? product.code.gtin);
90
+ product.code.set('sku', validator.code.sku ?? product.code.sku);
91
+ product.code.set('mpn', validator.code.mpn ?? product.code.mpn);
92
+
93
+ product.dimension.set('depth', validator.dimension.depth ?? product.dimension.depth);
94
+ product.dimension.set('height', validator.dimension.height ?? product.dimension.height);
95
+ product.dimension.set('weight', validator.dimension.weight ?? product.dimension.weight);
96
+ product.dimension.set('width', validator.dimension.width ?? product.dimension.width);
97
+
98
+ await this.$event.emitAsync(ProductEvent.LINK_FILES, product);
99
+
100
+ await product.save();
101
+
102
+ return product;
103
+ }
104
+
105
+ public async delete(id: string) {
106
+ return this.$delete(id);
107
+ }
108
+ }
@@ -0,0 +1,15 @@
1
+ import { SchemaFactory } from '@nestjs/mongoose';
2
+ import { ProductEntity } from './product.entity';
3
+
4
+ export const ProductSchema = SchemaFactory.createForClass(ProductEntity);
5
+
6
+ ProductSchema.virtual('picture').get<ProductEntity>(function () {
7
+ const [first] = this.files
8
+
9
+ return first?.url;
10
+ });
11
+
12
+ ProductSchema.index({
13
+ name: 'text',
14
+ description: 'text',
15
+ })
@@ -0,0 +1,71 @@
1
+ import { Type } from 'class-transformer';
2
+ import {
3
+ IsBoolean,
4
+ IsMongoId,
5
+ IsNotEmpty,
6
+ IsNotEmptyObject,
7
+ IsOptional,
8
+ IsString,
9
+ ValidateNested
10
+ } from 'class-validator';
11
+ import 'reflect-metadata';
12
+ import { ProductCodeValidator } from './validators/code.validator';
13
+ import { ProductDimensionValidator } from './validators/dimension.validator';
14
+ import { ProductPriceValidator } from './validators/price.validator';
15
+ import { ProductSeoValidator } from './validators/seo.validator';
16
+ import { AbstractValidator } from "../../../abstract/abstract.validator";
17
+
18
+ export class CreateProductValidator extends AbstractValidator {
19
+ @IsString()
20
+ @IsNotEmpty()
21
+ public name = String();
22
+
23
+ @IsString()
24
+ @IsOptional()
25
+ public description = String();
26
+
27
+ @IsString()
28
+ public measurement = String();
29
+
30
+ @IsBoolean()
31
+ @IsOptional()
32
+ public active = Boolean(1);
33
+
34
+ @IsOptional()
35
+ @IsMongoId()
36
+ public category?: string = null;
37
+
38
+ @IsOptional()
39
+ @IsMongoId()
40
+ public brand?: string = null;
41
+
42
+ @IsMongoId({ each: true })
43
+ @IsOptional()
44
+ public files: string[] = [];
45
+
46
+ @IsString({ each: true })
47
+ public hashtags: string[] = [];
48
+
49
+ @ValidateNested()
50
+ @IsNotEmptyObject()
51
+ @Type(() => ProductPriceValidator)
52
+ public price = new ProductPriceValidator();
53
+
54
+ @ValidateNested()
55
+ @Type(() => ProductSeoValidator)
56
+ public seo = new ProductSeoValidator();
57
+
58
+ @ValidateNested()
59
+ @Type(() => ProductDimensionValidator)
60
+ public dimension = new ProductDimensionValidator();
61
+
62
+ @ValidateNested()
63
+ @Type(() => ProductCodeValidator)
64
+ public code = new ProductCodeValidator();
65
+ }
66
+
67
+ export class UpdateProductValidator extends CreateProductValidator {
68
+ @IsString()
69
+ @IsOptional()
70
+ public name: string;
71
+ }
@@ -0,0 +1,15 @@
1
+ import { IsOptional, IsString } from 'class-validator';
2
+
3
+ export class ProductCodeValidator {
4
+ @IsString()
5
+ @IsOptional()
6
+ public sku? = String();
7
+
8
+ @IsString()
9
+ @IsOptional()
10
+ public gtin? = String();
11
+
12
+ @IsString()
13
+ @IsOptional()
14
+ public mpn? = String();
15
+ }