impl modulo orde node 22

This commit is contained in:
JurTI-BR
2025-04-01 14:46:39 -03:00
parent 4522575701
commit f8bea4114f
22 changed files with 656 additions and 99 deletions

3
.env
View File

@@ -11,8 +11,9 @@ POSTGRES_USER=ti
POSTGRES_PASSWORD=ti
POSTGRES_DB=ksdb
JWT_SECRET=4557C0D7-DFB0-40DA-BF83-91A75103F7A9
JWT_EXPIRES_IN=8h

217
package-lock.json generated
View File

@@ -10,6 +10,7 @@
"license": "UNLICENSED",
"dependencies": {
"@nestjs/axios": "^4.0.0",
"@nestjs/bull": "^11.0.2",
"@nestjs/common": "^11.0.12",
"@nestjs/config": "^4.0.2",
"@nestjs/core": "^11.0.12",
@@ -27,6 +28,7 @@
"@types/estree": "^1.0.7",
"aws-sdk": "^2.1692.0",
"axios": "^1.8.4",
"bullmq": "^5.45.2",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"fs": "0.0.1-security",
@@ -1582,6 +1584,84 @@
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz",
"integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw=="
},
"node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz",
"integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz",
"integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz",
"integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz",
"integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz",
"integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz",
"integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@nestjs/axios": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-4.0.0.tgz",
@@ -1593,6 +1673,34 @@
"rxjs": "^7.0.0"
}
},
"node_modules/@nestjs/bull": {
"version": "11.0.2",
"resolved": "https://registry.npmjs.org/@nestjs/bull/-/bull-11.0.2.tgz",
"integrity": "sha512-RjyP9JZUuLmMhmq1TMNIZqolkAd14az1jyXMMVki+C9dYvaMjWzBSwcZAtKs9Pk15Rm7qN1xn3R11aMV2Xv4gg==",
"license": "MIT",
"dependencies": {
"@nestjs/bull-shared": "^11.0.2",
"tslib": "2.8.1"
},
"peerDependencies": {
"@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0",
"@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0",
"bull": "^3.3 || ^4.0.0"
}
},
"node_modules/@nestjs/bull-shared": {
"version": "11.0.2",
"resolved": "https://registry.npmjs.org/@nestjs/bull-shared/-/bull-shared-11.0.2.tgz",
"integrity": "sha512-dFlttJvBqIFD6M8JVFbkrR4Feb39OTAJPJpFVILU50NOJCM4qziRw3dSNG84Q3v+7/M6xUGMFdZRRGvBBKxoSA==",
"license": "MIT",
"dependencies": {
"tslib": "2.8.1"
},
"peerDependencies": {
"@nestjs/common": "^10.0.0 || ^11.0.0",
"@nestjs/core": "^10.0.0 || ^11.0.0"
}
},
"node_modules/@nestjs/cli": {
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.5.tgz",
@@ -3811,6 +3919,34 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
},
"node_modules/bullmq": {
"version": "5.45.2",
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.45.2.tgz",
"integrity": "sha512-wHZfcD4z4aLolxREmwNNDSbfh7USeq2e3yu5W2VGkzHMUcrH0fzZuRuCMsjD0XKS9ViK1U854oM9yWR6ftPeDA==",
"license": "MIT",
"dependencies": {
"cron-parser": "^4.9.0",
"ioredis": "^5.4.1",
"msgpackr": "^1.11.2",
"node-abort-controller": "^3.1.1",
"semver": "^7.5.4",
"tslib": "^2.0.0",
"uuid": "^9.0.0"
}
},
"node_modules/bullmq/node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@@ -4508,6 +4644,18 @@
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"node_modules/cron-parser": {
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz",
"integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==",
"license": "MIT",
"dependencies": {
"luxon": "^3.2.1"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -4743,6 +4891,16 @@
"node": ">= 0.8"
}
},
"node_modules/detect-libc": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
"integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
"license": "Apache-2.0",
"optional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/detect-newline": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
@@ -6314,6 +6472,7 @@
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.6.0.tgz",
"integrity": "sha512-tBZlIIWbndeWBWCXWZiqtOF/yxf6yZX3tAlTJ7nfo5jhd6dctNxF7QnYlZLZ1a0o0pDoen7CgZqO+zjNaFbJAg==",
"license": "MIT",
"dependencies": {
"@ioredis/commands": "^1.1.1",
"cluster-key-slot": "^1.1.0",
@@ -7984,6 +8143,15 @@
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
},
"node_modules/luxon": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.6.1.tgz",
"integrity": "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==",
"license": "MIT",
"engines": {
"node": ">=12"
}
},
"node_modules/magic-string": {
"version": "0.25.7",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
@@ -8222,6 +8390,37 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/msgpackr": {
"version": "1.11.2",
"resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.2.tgz",
"integrity": "sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==",
"license": "MIT",
"optionalDependencies": {
"msgpackr-extract": "^3.0.2"
}
},
"node_modules/msgpackr-extract": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz",
"integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"dependencies": {
"node-gyp-build-optional-packages": "5.2.2"
},
"bin": {
"download-msgpackr-prebuilds": "bin/download-prebuilds.js"
},
"optionalDependencies": {
"@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3",
"@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3",
"@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3",
"@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3",
"@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3",
"@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3"
}
},
"node_modules/multer": {
"version": "1.4.5-lts.2",
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz",
@@ -8318,8 +8517,7 @@
"node_modules/node-abort-controller": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
"integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
"dev": true
"integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ=="
},
"node_modules/node-emoji": {
"version": "1.11.0",
@@ -8330,6 +8528,21 @@
"lodash": "^4.17.21"
}
},
"node_modules/node-gyp-build-optional-packages": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
"integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==",
"license": "MIT",
"optional": true,
"dependencies": {
"detect-libc": "^2.0.1"
},
"bin": {
"node-gyp-build-optional-packages": "bin.js",
"node-gyp-build-optional-packages-optional": "optional.js",
"node-gyp-build-optional-packages-test": "build-test.js"
}
},
"node_modules/node-int64": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",

View File

@@ -21,6 +21,7 @@
},
"dependencies": {
"@nestjs/axios": "^4.0.0",
"@nestjs/bull": "^11.0.2",
"@nestjs/common": "^11.0.12",
"@nestjs/config": "^4.0.2",
"@nestjs/core": "^11.0.12",
@@ -38,6 +39,7 @@
"@types/estree": "^1.0.7",
"aws-sdk": "^2.1692.0",
"axios": "^1.8.4",
"bullmq": "^5.45.2",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"fs": "0.0.1-security",

View File

@@ -8,7 +8,7 @@
import { ProductsModule } from './products/products.module';
import { AuthModule } from './auth/auth/auth.module';
import { DataConsultModule } from './data-consult/data-consult.module';
import { OrdersModule } from './orders/orders.module';
import { OrdersModule } from './orders/modules/orders.module';
import { OcorrencesController } from './crm/occurrences/ocorrences.controller';
import { OccurrencesModule } from './crm/occurrences/occurrences.module';
import { ReasonTableModule } from './crm/reason-table/reason-table.module';
@@ -51,6 +51,6 @@
OrdersModule,
],
controllers: [OcorrencesController, LogisticController ],
providers: [ LogisticService, ],
providers: [ LogisticService, ],
})
export class AppModule {}

View File

@@ -7,7 +7,7 @@ import {
} from '@nestjs/common';
import { CommandBus } from '@nestjs/cqrs';
import { CqrsModule } from '@nestjs/cqrs';
import { AuthenticateUserCommand } from './authenticate-user.command';
import { AuthenticateUserCommand } from './commands/authenticate-user.command';
import { LoginResponseDto } from './dto/LoginResponseDto';
import { LoginDto } from './dto/login.dto';
import { ResultModel } from 'src/core/models/result.model';

View File

@@ -8,7 +8,7 @@ import { UsersModule } from '../users/users.module';
import { AuthController } from './auth.controller';
import { CqrsModule } from '@nestjs/cqrs';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuthenticateUserHandler } from './authenticate-user.service';
import { AuthenticateUserHandler } from './commands/authenticate-user.service';
@Module({
imports: [
@@ -29,7 +29,7 @@ import { AuthenticateUserHandler } from './authenticate-user.service';
UsersModule,
],
controllers: [AuthController],
providers: [AuthService, JwtStrategy, AuthenticateUserHandler],
providers: [AuthService, JwtStrategy],
exports: [AuthService],
})
export class AuthModule {}

View File

@@ -1,7 +1,7 @@
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import { AuthenticateUserCommand } from './authenticate-user.command';
import { UserRepository } from '../users/UserRepository';
import { Result } from '../models/result';
import { UserRepository } from '../../users/UserRepository';
import { Result } from '../../models/result';
import { Injectable } from '@nestjs/common';
import { UserModel } from 'src/core/models/user.model';

View File

@@ -1,9 +1,8 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm'; // <-- importe aqui
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersService } from './users.service';
import { UserRepository } from './UserRepository';
import { AuthenticateUserHandler } from '../auth/authenticate-user.service';
import { AuthenticateUserHandler } from '../auth/commands/authenticate-user.service';
import { ResetPasswordService } from './reset-password.service';
import { ChangePasswordService } from './change-password.service';
import { EmailService } from './email.service';

View File

@@ -1,8 +1,8 @@
import { Injectable } from '@nestjs/common';
import { AuthenticateUserHandler } from '../auth/authenticate-user.service';
import { AuthenticateUserHandler } from '../auth/commands/authenticate-user.service';
import { ResetPasswordService } from './reset-password.service';
import { ChangePasswordService } from './change-password.service';
import { AuthenticateUserCommand } from '../auth/authenticate-user.command';
import { AuthenticateUserCommand } from '../auth/commands/authenticate-user.command';

View File

@@ -98,7 +98,7 @@ export class LogisticService {
}
async getDeliveries(placa: string) {
const dataSource = new DataSource(createPostgresConfig(this.configService));
const dataSource = new DataSource(createOracleConfig(this.configService));
await dataSource.initialize();
const queryRunner = dataSource.createQueryRunner();
await queryRunner.connect();

View File

@@ -9,8 +9,6 @@ async function bootstrap() {
app.useGlobalInterceptors(new ResponseInterceptor());
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,

View File

@@ -1,14 +1,113 @@
import { Injectable } from '@nestjs/common';
import { Injectable, Inject } from '@nestjs/common';
import { FindOrdersDto } from '../dto/find-orders.dto';
import { InvoiceDto } from '../dto/find-invoice.dto';
import { CutItemDto } from '../dto/CutItemDto';
import { OrdersRepository } from '../repositories/orders.repository';
import { OrderItemDto } from '../dto/OrderItemDto';
import { IRedisClient } from 'src/core/configs/cache/IRedisClient';
import { RedisClientToken } from 'src/core/configs/cache/redis-client.adapter.provider';
import { getOrSetCache } from 'src/shared/cache.util';
import { createHash } from 'crypto';
@Injectable()
export class OrdersService {
constructor(private readonly ordersRepository: OrdersRepository) {}
private readonly TTL_ORDERS = 60 * 10; // 10 minutos
private readonly TTL_INVOICE = 60 * 60; // 1 hora
private readonly TTL_ITENS = 60 * 10; // 10 minutos
constructor(
private readonly ordersRepository: OrdersRepository,
@Inject(RedisClientToken) private readonly redisClient: IRedisClient,
) {}
/**
* Buscar pedidos com cache baseado nos filtros
*/
async findOrders(query: FindOrdersDto) {
return await this.ordersRepository.findOrders(query);
const key = `orders:query:${this.hashObject(query)}`;
return getOrSetCache(
this.redisClient,
key,
this.TTL_ORDERS,
() => this.ordersRepository.findOrders(query),
);
}
/**
* Buscar nota fiscal por chave NFe com cache
*/
async findInvoice(chavenfe: string): Promise<InvoiceDto> {
const key = `orders:invoice:${chavenfe}`;
return getOrSetCache(this.redisClient, key, this.TTL_INVOICE, async () => {
const invoiceData = await this.ordersRepository.findInvoice(chavenfe);
const invoice: InvoiceDto = {
storeId: invoiceData.storeId,
invoiceDate: new Date(invoiceData.invoiceDate),
orderId: invoiceData.orderId,
invoiceId: invoiceData.invoiceId,
transactionId: invoiceData.transactionId,
customerId: invoiceData.customerId,
customer: invoiceData.customer,
sellerId: invoiceData.sellerId,
sellerName: invoiceData.sellerName,
itensQt: invoiceData.itensQt,
itens: invoiceData.itens,
};
return invoice;
});
}
/**
* Buscar itens de pedido com cache
*/
async getItens(orderId: string): Promise<OrderItemDto[]> {
const key = `orders:itens:${orderId}`;
return getOrSetCache(this.redisClient, key, this.TTL_ITENS, async () => {
const itens = await this.ordersRepository.getItens(orderId);
return itens.map(item => ({
productId: Number(item.productId),
description: item.description,
pacth: item.pacth,
color: item.color,
stockId: Number(item.stockId),
quantity: Number(item.quantity),
salePrice: Number(item.salePrice),
deliveryType: item.deliveryType,
total: Number(item.total),
weight: Number(item.weight),
department: item.department,
brand: item.brand,
}));
});
}
async getCutItens(orderId: string): Promise<CutItemDto[]> {
const itens = await this.ordersRepository.getCutItens(orderId);
return itens.map(item => ({
productId: Number(item.productId),
description: item.description,
pacth: item.pacth,
stockId: Number(item.stockId),
saleQuantity: Number(item.saleQuantity),
cutQuantity: Number(item.cutQuantity),
separedQuantity: Number(item.separedQuantity),
}));
}
/**
* Utilitário para gerar hash MD5 de objetos
*/
private hashObject(obj: any): string {
const str = JSON.stringify(obj, Object.keys(obj).sort());
return createHash('md5').update(str).digest('hex');
}
}

View File

@@ -0,0 +1,76 @@
import {
Controller,
Get,
Param,
Query,
UsePipes,
UseGuards,
UseInterceptors,
ValidationPipe,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { ResponseInterceptor } from '../../common/response.interceptor';
import { OrdersService } from '../application/orders.service';
import { FindOrdersDto } from '../dto/find-orders.dto';
import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard';
import { InvoiceDto } from '../dto/find-invoice.dto';
import { OrderItemDto } from "../dto/OrderItemDto";
import { CutItemDto } from '../dto/CutItemDto';
@ApiTags('Orders')
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@UseInterceptors(ResponseInterceptor)
@Controller('api/v1/orders')
export class OrdersController {
constructor(private readonly ordersService: OrdersService) {}
@Get('find')
@UsePipes(new ValidationPipe({ transform: true }))
findOrders(@Query() query: FindOrdersDto) {
return this.ordersService.findOrders(query);
}
@Get('invoice/:chavenfe')
@ApiOperation({ summary: 'Busca NF pela chave' })
@UsePipes(new ValidationPipe({ transform: true }))
async getInvoice(@Param('chavenfe') chavenfe: string): Promise<InvoiceDto> {
try {
return await this.ordersService.findInvoice(chavenfe);
} catch (error) {
throw new HttpException(
error.message || 'Erro ao buscar nota fiscal',
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
@Get('itens/:orderId')
@ApiOperation({ summary: 'Busca PELO numero do pedido' })
@UsePipes(new ValidationPipe({ transform: true }))
async getItens(@Param('orderId') orderId: string): Promise<OrderItemDto[]> {
try {
return await this.ordersService.getItens(orderId);
} catch (error) {
throw new HttpException(
error.message || 'Erro ao buscar itens do pedido',
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
@Get('cut-itens/:orderId')
@ApiOperation({ summary: 'Busca itens cortados do pedido' })
@UsePipes(new ValidationPipe({ transform: true }))
async getCutItens(@Param('orderId') orderId: string): Promise<CutItemDto[]> {
try {
return await this.ordersService.getCutItens(orderId);
} catch (error) {
throw new HttpException(
error.message || 'Erro ao buscar itens cortados',
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
}

View File

@@ -0,0 +1,10 @@
export class CutItemDto {
productId: number;
description: string;
pacth: string;
stockId: number;
saleQuantity: number;
cutQuantity: number;
separedQuantity: number;
}

View File

@@ -0,0 +1,14 @@
export class OrderItemDto {
productId: number;
description: string;
pacth: string;
color: string;
stockId: number;
quantity: number;
salePrice: number;
deliveryType: string;
total: number;
weight: number;
department: string;
brand: string;
}

View File

@@ -0,0 +1,29 @@
export class FindInvoiceDto {
chavenfe: string;
}
export class InvoiceItemDto {
productId: number;
productName: string;
package: string;
qt: number;
ean: string;
multiple: number;
productType: string;
image: string;
}
export class InvoiceDto {
storeId: number;
invoiceDate: Date;
orderId: number;
invoiceId: number;
transactionId: number;
customerId: number;
customer: string;
sellerId: number;
sellerName: string;
itensQt: number;
itens: InvoiceItemDto[];
}

View File

@@ -12,13 +12,13 @@ export class FindOrdersDto {
@IsOptional()
@IsString()
@ApiPropertyOptional()
storeId?: string;
codfilial?: string;
@IsOptional()
@IsString()
@ApiPropertyOptional()
storeStockId?: string;
filialretira?: string;
@IsOptional()
@IsNumber()

View File

@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { OrdersController } from '../controllers/orders.controller';
import { OrdersService } from '../application/orders.service';
import { OrdersRepository } from '../repositories/orders.repository';
@Module({
imports: [],
controllers: [OrdersController],
providers: [OrdersService, OrdersRepository],
})
export class OrdersModule {}

View File

@@ -1,33 +0,0 @@
// src/modules/orders/interfaces/controllers/orders.controller.ts
import {
Body,
Controller,
Get,
Param,
Post,
Query,
UsePipes,
UseGuards,
ValidationPipe,
} from '@nestjs/common';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { ResponseInterceptor } from '../common/response.interceptor';
import { OrdersService } from './application/orders.service';
import { FindOrdersDto } from './dto/find-orders.dto';
import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard';
@ApiTags('Orders')
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Controller('api/v1/orders')
export class OrdersController {
constructor(private readonly ordersService: OrdersService) {}
@Get('find')
@UsePipes(new ValidationPipe({ transform: true }))
findOrders(@Query() query: FindOrdersDto) {
return this.ordersService.findOrders(query);
}
}

View File

@@ -1,16 +0,0 @@
// src/modules/orders/orders.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { OrdersService } from './application/orders.service';
import { OrdersController } from './orders.controller';
import { OrdersRepository } from './repositories/orders.repository';
@Module({
imports: [
TypeOrmModule,
],
controllers: [OrdersController],
providers: [OrdersService, OrdersRepository],
})
export class OrdersModule {}

View File

@@ -1,19 +1,19 @@
// src/modules/orders/infrastructure/repositories/orders.repository.ts
import { Injectable, HttpException, HttpStatus } from "@nestjs/common";
import { DataSource } from "typeorm";
import { InjectDataSource } from "@nestjs/typeorm";
import { FindOrdersDto } from "../dto/find-orders.dto";
import { OrderItemDto } from "../dto/OrderItemDto";
import { CutItemDto } from '../dto/CutItemDto';
import { Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { InjectDataSource } from '@nestjs/typeorm';
import { createOracleConfig } from '../../core/configs/typeorm.oracle.config';
import { FindOrdersDto } from '../dto/find-orders.dto';
@Injectable()
export class OrdersRepository {
constructor(
@InjectDataSource('oracle') private readonly dataSource: DataSource
@InjectDataSource("oracle") private readonly dataSource: DataSource
) {}
async findOrders(query: FindOrdersDto): Promise<any[]> {
await this.dataSource.initialize();
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
@@ -143,11 +143,13 @@ export class OrdersRepository {
const conditions: string[] = [];
if (query.storeId) {
if (query.codfilial) {
conditions.push(`AND PCPEDC.CODFILIAL = :storeId`);
}
if (query.storeStockId) {
conditions.push(`AND EXISTS(SELECT 1 FROM PCPEDI WHERE PCPEDI.NUMPED = PCPEDC.NUMPED AND PCPEDI.CODFILIALRETIRA = :storeStockId)`);
if (query.filialretira) {
conditions.push(
`AND EXISTS(SELECT 1 FROM PCPEDI WHERE PCPEDI.NUMPED = PCPEDC.NUMPED AND PCPEDI.CODFILIALRETIRA = :storeStockId)`
);
}
if (query.sellerId) {
conditions.push(`AND PCPEDC.CODUSUR = :sellerId`);
@@ -159,42 +161,65 @@ export class OrdersRepository {
conditions.push(`AND PCPEDC.CODCOB = :billingId`);
}
if (query.orderId) {
conditions.push(`AND (PCPEDC.NUMPED = :orderId OR PCPEDC.NUMPEDENTFUT = :orderId)`);
conditions.push(
`AND (PCPEDC.NUMPED = :orderId OR PCPEDC.NUMPEDENTFUT = :orderId)`
);
}
if (query.invoiceId) {
conditions.push(`AND PCPEDC.NUMNOTA = :invoiceId`);
}
if (query.productId) {
conditions.push(`AND EXISTS(SELECT 1 FROM PCPEDI WHERE PCPEDI.NUMPED = PCPEDC.NUMPED AND PCPEDI.CODPROD = :productId)`);
conditions.push(
`AND EXISTS(SELECT 1 FROM PCPEDI WHERE PCPEDI.NUMPED = PCPEDC.NUMPED AND PCPEDI.CODPROD = :productId)`
);
}
if (query.createDateIni) {
conditions.push(`AND PCPEDC.DATA >= TO_DATE(:createDateIni, 'YYYY-MM-DD')`);
conditions.push(
`AND PCPEDC.DATA >= TO_DATE(:createDateIni, 'YYYY-MM-DD')`
);
}
if (query.createDateEnd) {
conditions.push(`AND PCPEDC.DATA <= TO_DATE(:createDateEnd, 'YYYY-MM-DD')`);
conditions.push(
`AND PCPEDC.DATA <= TO_DATE(:createDateEnd, 'YYYY-MM-DD')`
);
}
if (query.invoiceDateIni) {
conditions.push(`AND PCPEDC.DTFAT >= TO_DATE(:invoiceDateIni, 'YYYY-MM-DD')`);
conditions.push(
`AND PCPEDC.DTFAT >= TO_DATE(:invoiceDateIni, 'YYYY-MM-DD')`
);
}
if (query.invoiceDateEnd) {
conditions.push(`AND PCPEDC.DTFAT <= TO_DATE(:invoiceDateEnd, 'YYYY-MM-DD')`);
conditions.push(
`AND PCPEDC.DTFAT <= TO_DATE(:invoiceDateEnd, 'YYYY-MM-DD')`
);
}
if (query.shippimentId) {
conditions.push(`AND PCPEDC.NUMCAR = :shippimentId`);
}
if (query.deliveryType) {
const types = query.deliveryType.split(',').map(t => `'${t}'`).join(',');
conditions.push(`AND EXISTS(SELECT 1 FROM PCPEDI WHERE PCPEDI.NUMPED = PCPEDC.NUMPED AND PCPEDI.TIPOENTREGA IN (${types}))`);
const types = query.deliveryType
.split(",")
.map((t) => `'${t}'`)
.join(",");
conditions.push(
`AND EXISTS(SELECT 1 FROM PCPEDI WHERE PCPEDI.NUMPED = PCPEDC.NUMPED AND PCPEDI.TIPOENTREGA IN (${types}))`
);
}
if (query.status) {
const statusList = query.status.split(',').map(s => `'${s}'`).join(',');
const statusList = query.status
.split(",")
.map((s) => `'${s}'`)
.join(",");
conditions.push(`AND PCPEDC.POSICAO IN (${statusList})`);
}
if (query.type) {
const types = query.type.split(',').map(t => `'${t}'`).join(',');
const types = query.type
.split(",")
.map((t) => `'${t}'`)
.join(",");
conditions.push(`AND PCPEDC.CONDVENDA IN (${types})`);
}
if (query.onlyPendentingTransfer === 'S') {
if (query.onlyPendentingTransfer === "S") {
conditions.push(`
AND NOT EXISTS(SELECT 1 FROM PCNFENT, PCFILIAL
WHERE PCFILIAL.CODIGO = PCPEDC.CODFILIAL
@@ -202,8 +227,8 @@ export class OrdersRepository {
AND PCNFENT.NUMNOTA = PCPEDC.NUMNOTA)`);
}
sql += '\n' + conditions.join('\n');
sql += '\nAND ROWNUM < 5000';
sql += "\n" + conditions.join("\n");
sql += "\nAND ROWNUM < 5000";
const orders = await queryRunner.manager.query(sql);
return orders;
@@ -212,4 +237,132 @@ export class OrdersRepository {
await this.dataSource.destroy();
}
}
async findInvoice(chavenfe: string): Promise<any> {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
try {
const sql = `
SELECT
pcnfsaid.codfilial AS "storeId",
pcnfsaid.dtsaida AS "invoiceDate",
pcnfsaid.numped AS "orderId",
pcnfsaid.numnota AS "invoiceId",
pcnfsaid.numtransvenda AS "transactionId",
pcnfsaid.codcli AS "customerId",
pcclient.cliente AS "customer",
pcnfsaid.codusur AS "sellerId",
pcusuari.nome AS "sellerName",
(SELECT SUM(pcmov.qt) FROM pcmov WHERE pcmov.numtransvenda = pcnfsaid.numtransvenda) AS "itensQt",
NULL AS "itens"
FROM pcnfsaid
JOIN pcclient ON pcnfsaid.codcli = pcclient.codcli
JOIN pcusuari ON pcnfsaid.codusur = pcusuari.codusur
WHERE pcnfsaid.chavenfe = '${chavenfe}'
`;
const invoice = await queryRunner.manager.query(sql);
if (!invoice || invoice.length === 0) {
throw new HttpException('Nota fiscal não foi localizada no sistema', HttpStatus.BAD_REQUEST);
}
const sqlItem = `
SELECT
pcmov.codprod AS "productId",
pcprodut.descricao AS "productName",
pcprodut.embalagem AS "package",
pcmov.qt AS "qt",
pcprodut.codauxiliar AS "ean",
pcprodut.multiplo AS "multiple",
pcprodut.tipoproduto AS "productType",
REPLACE(
CASE
WHEN INSTR(pcprodut.URLIMAGEM, ';') > 0
THEN SUBSTR(pcprodut.URLIMAGEM,1,INSTR(pcprodut.URLIMAGEM, ';') - 1)
WHEN pcprodut.URLIMAGEM IS NOT NULL
THEN pcprodut.URLIMAGEM
ELSE NULL
END,
'167.249.211.178:8001',
'10.1.1.191'
) AS "image"
FROM pcmov
JOIN pcprodut ON pcmov.codprod = pcprodut.codprod
WHERE pcmov.numtransvenda = ${invoice[0].transactionId}
`;
const itens = await queryRunner.manager.query(sqlItem);
invoice[0].itens = itens;
return invoice[0];
} finally {
await queryRunner.release();
}
}
async getItens(orderId: string): Promise<OrderItemDto[]> {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
try {
const sql = `SELECT PCPEDI.CODPROD as "productId"
, PCPRODUT.DESCRICAO as "description"
, PCPRODUT.EMBALAGEM as "pacth"
, NVL(PCPEDI.COMPLEMENTO,
( SELECT TV7I.COMPLEMENTO FROM PCPEDC, PCPEDC TV7, PCPEDITEMP TV7I
WHERE TV7.NUMPED = PCPEDC.NUMPEDENTFUT
AND PCPEDC.NUMPED = PCPEDI.NUMPED
AND TV7.NUMPEDRCA = TV7I.NUMPEDRCA
AND TV7I.CODPROD = PCPEDI.codprod
AND TV7I.NUMSEQ = PCPEDI.NUMSEQ ) ) as "color"
, PCPEDI.CODFILIALRETIRA as "stockId"
, PCPEDI.QT as "quantity"
, PCPEDI.PVENDA as "salePrice"
, CASE WHEN PCPEDI.TIPOENTREGA = 'RI' THEN 'RETIRA IMEDIATA'
WHEN PCPEDI.TIPOENTREGA = 'RP' THEN 'RETIRA POSTERIOR'
WHEN PCPEDI.TIPOENTREGA = 'EN' THEN 'ENTREGA'
WHEN PCPEDI.TIPOENTREGA = 'EF' THEN 'ENTREGA FUTURA'
END as "deliveryType"
, ( PCPEDI.QT *
PCPEDI.PVENDA ) as "total"
, ( PCPEDI.QT * PCPRODUT.PESOBRUTO ) as "weigth"
, PCDEPTO.DESCRICAO as "department"
, PCMARCA.MARCA as "brand"
FROM PCPEDI, PCPRODUT, PCDEPTO, PCMARCA
WHERE PCPEDI.CODPROD = PCPRODUT.CODPROD
AND PCPRODUT.CODEPTO = PCDEPTO.CODEPTO
AND PCPRODUT.CODMARCA = PCMARCA.CODMARCA
AND PCPEDI.NUMPED = ${orderId}`;
const itens = await queryRunner.manager.query(sql);
return itens;
} finally {
await queryRunner.release();
}
}
async getCutItens(orderId: string): Promise<CutItemDto[]> {
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
try {
const sql = `
SELECT
PCCORTEI.CODPROD as "productId",
PCPRODUT.DESCRICAO as "description",
PCPRODUT.EMBALAGEM as "pacth",
PCCORTEI.CODFILIAL as "stockId",
(PCCORTEI.QTSEPARADA + PCCORTEI.QTCORTADA) as "saleQuantity",
PCCORTEI.QTCORTADA as "cutQuantity",
PCCORTEI.QTSEPARADA as "separedQuantity"
FROM PCCORTEI
JOIN PCPRODUT ON PCCORTEI.CODPROD = PCPRODUT.CODPROD
WHERE PCCORTEI.NUMPED = ${orderId}
`;
const itens = await queryRunner.manager.query(sql);
return itens;
} finally {
await queryRunner.release();
}
}
}