feat: enhance business activities and sale invoices handling with new query constants and pagination support
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
export enum GoldKarat {
|
export enum GoldKarat {
|
||||||
KARAT_18 = '18',
|
KARAT_18 = 'KARAT_18',
|
||||||
KARAT_21 = '21',
|
KARAT_21 = 'KARAT_21',
|
||||||
KARAT_24 = '24',
|
KARAT_24 = 'KARAT_24',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum TspProviderRequestType {
|
export enum TspProviderRequestType {
|
||||||
@@ -16,16 +16,16 @@ export enum SKUGuildType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const UploadedFileTypes = {
|
export const UploadedFileTypes = {
|
||||||
GOOD: 'good',
|
GOOD: 'GOOD',
|
||||||
SERVICE: 'service',
|
SERVICE: 'SERVICE',
|
||||||
PROFILE_AVATAR: 'profile_avatar',
|
PROFILE_AVATAR: 'PROFILE_AVATAR',
|
||||||
PARTNER_LOGO: 'partner_logo',
|
PARTNER_LOGO: 'PARTNER_LOGO',
|
||||||
}
|
}
|
||||||
export type UploadedFileTypes = (typeof UploadedFileTypes)[keyof typeof UploadedFileTypes]
|
export type UploadedFileTypes = (typeof UploadedFileTypes)[keyof typeof UploadedFileTypes]
|
||||||
|
|
||||||
export const TspProviderCustomerType = {
|
export const TspProviderCustomerType = {
|
||||||
UNKNOWN: 'Unknown',
|
UNKNOWN: 'UNKNOWN',
|
||||||
KNOWN: 'Known',
|
KNOWN: 'KNOWN',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TspProviderCustomerType =
|
export type TspProviderCustomerType =
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { BusinessActivitySelect } from '@/generated/prisma/models'
|
||||||
|
|
||||||
|
export const summarySelect: BusinessActivitySelect = {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
economic_code: true,
|
||||||
|
created_at: true,
|
||||||
|
fiscal_id: true,
|
||||||
|
invoice_number_sequence: true,
|
||||||
|
partner_token: true,
|
||||||
|
guild: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
code: true,
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
license_activation: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
starts_at: true,
|
||||||
|
expires_at: true,
|
||||||
|
license: {
|
||||||
|
select: {
|
||||||
|
activation: {
|
||||||
|
select: {
|
||||||
|
_count: {
|
||||||
|
select: {
|
||||||
|
account_allocations: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const select: BusinessActivitySelect = {
|
||||||
|
...summarySelect,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mappedData = (businessActivity: any) => {
|
||||||
|
const { license_activation, ...rest } = businessActivity
|
||||||
|
const { license, ...license_activation_rest } = license_activation
|
||||||
|
const { _count } = license.activation
|
||||||
|
|
||||||
|
return {
|
||||||
|
...rest,
|
||||||
|
license_info: {
|
||||||
|
...license_activation_rest,
|
||||||
|
accounts_limit: _count.account_allocations,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import * as BUSINESS_ACTIVITIES from './businessActivities'
|
||||||
import * as CONSUMER from './consumer'
|
import * as CONSUMER from './consumer'
|
||||||
import * as LICENSE_ACTIVATION from './licenseActivation'
|
import * as LICENSE_ACTIVATION from './licenseActivation'
|
||||||
import * as POS from './pos'
|
import * as POS from './pos'
|
||||||
@@ -8,4 +9,5 @@ export const QUERY_CONSTANTS = {
|
|||||||
CONSUMER,
|
CONSUMER,
|
||||||
LICENSE_ACTIVATION,
|
LICENSE_ACTIVATION,
|
||||||
SALE_INVOICE,
|
SALE_INVOICE,
|
||||||
|
BUSINESS_ACTIVITIES,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { QUERY_CONSTANTS } from '@/common/queryConstants'
|
||||||
import { BusinessActivitySelect } from '@/generated/prisma/models'
|
import { BusinessActivitySelect } from '@/generated/prisma/models'
|
||||||
import { PrismaService } from '@/prisma/prisma.service'
|
import { PrismaService } from '@/prisma/prisma.service'
|
||||||
import { Injectable } from '@nestjs/common'
|
import { Injectable } from '@nestjs/common'
|
||||||
@@ -7,45 +8,35 @@ export class BusinessActivitiesQueryService {
|
|||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
readonly baseSelect: BusinessActivitySelect = {
|
readonly baseSelect: BusinessActivitySelect = {
|
||||||
id: true,
|
...QUERY_CONSTANTS.BUSINESS_ACTIVITIES.summarySelect,
|
||||||
name: true,
|
|
||||||
economic_code: true,
|
|
||||||
created_at: true,
|
|
||||||
partner_token: true,
|
|
||||||
fiscal_id: true,
|
|
||||||
guild: {
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
code: true,
|
|
||||||
name: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
findAllByConsumer(consumer_id: string, select: BusinessActivitySelect) {
|
async findAllByConsumer(consumer_id: string, select: BusinessActivitySelect) {
|
||||||
return this.prisma.businessActivity.findMany({
|
const businessActivities = await this.prisma.businessActivity.findMany({
|
||||||
where: { consumer_id },
|
where: { consumer_id },
|
||||||
select,
|
select,
|
||||||
})
|
})
|
||||||
|
return businessActivities.map(QUERY_CONSTANTS.BUSINESS_ACTIVITIES.mappedData)
|
||||||
}
|
}
|
||||||
|
|
||||||
findOneByConsumer(
|
async findOneByConsumer(
|
||||||
consumer_id: string,
|
consumer_id: string,
|
||||||
id: string,
|
id: string,
|
||||||
select: BusinessActivitySelect,
|
select: BusinessActivitySelect,
|
||||||
orThrow = false,
|
orThrow = false,
|
||||||
) {
|
) {
|
||||||
if (orThrow) {
|
// if (orThrow) {
|
||||||
return this.prisma.businessActivity.findUniqueOrThrow({
|
// return await this.prisma.businessActivity.findUniqueOrThrow({
|
||||||
where: { consumer_id, id },
|
// where: { consumer_id, id },
|
||||||
select,
|
// select,
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
return this.prisma.businessActivity.findUnique({
|
const businessActivity = await this.prisma.businessActivity.findUnique({
|
||||||
where: { consumer_id, id },
|
where: { consumer_id, id },
|
||||||
select,
|
select,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return QUERY_CONSTANTS.BUSINESS_ACTIVITIES.mappedData(businessActivity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ export class SharedSaleInvoiceCreateService {
|
|||||||
const terminalInfo = rawPayments.terminals as TerminalPaymentInfo | undefined
|
const terminalInfo = rawPayments.terminals as TerminalPaymentInfo | undefined
|
||||||
|
|
||||||
const payments: NormalizedPayment[] = Object.entries(rawPayments)
|
const payments: NormalizedPayment[] = Object.entries(rawPayments)
|
||||||
.filter(([key]) => key !== 'terminals')
|
.filter(([key, value]) => key !== 'terminals' && value && Number(value) > 0)
|
||||||
.map(([key, value]) => ({
|
.map(([key, value]) => ({
|
||||||
method: paymentMethodMap[key.toLowerCase()],
|
method: paymentMethodMap[key.toLowerCase()],
|
||||||
amount: typeof value === 'number' ? value : Number(value || 0),
|
amount: typeof value === 'number' ? value : Number(value || 0),
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ export function translateEnumValue(
|
|||||||
enumKey: keyof typeof translates.enums,
|
enumKey: keyof typeof translates.enums,
|
||||||
value: string | null | undefined,
|
value: string | null | undefined,
|
||||||
): EnumTranslatedValue {
|
): EnumTranslatedValue {
|
||||||
|
console.log(enumKey, value)
|
||||||
|
|
||||||
const enumsRegistry = translates.enums as EnumsRegistry
|
const enumsRegistry = translates.enums as EnumsRegistry
|
||||||
const enumMap = enumsRegistry[String(enumKey)]
|
const enumMap = enumsRegistry[String(enumKey)]
|
||||||
|
|
||||||
|
|||||||
+10
@@ -63,6 +63,9 @@ async function bootstrap() {
|
|||||||
.map(s => s.trim())
|
.map(s => s.trim())
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
: []
|
: []
|
||||||
|
|
||||||
|
console.log('envOrigins', envOrigins)
|
||||||
|
|
||||||
const allowedOrigins = (envOrigins.length ? envOrigins : defaultOrigins).filter(Boolean)
|
const allowedOrigins = (envOrigins.length ? envOrigins : defaultOrigins).filter(Boolean)
|
||||||
|
|
||||||
const isOriginAllowed = (origin: string) => {
|
const isOriginAllowed = (origin: string) => {
|
||||||
@@ -74,6 +77,9 @@ async function bootstrap() {
|
|||||||
const rootDomain = pattern.slice(2)
|
const rootDomain = pattern.slice(2)
|
||||||
return hostname === rootDomain || hostname.endsWith(`.${rootDomain}`)
|
return hostname === rootDomain || hostname.endsWith(`.${rootDomain}`)
|
||||||
}
|
}
|
||||||
|
console.log('origin', origin, 'pattern', pattern, 'hostname', hostname)
|
||||||
|
console.log(hostname === pattern || origin === pattern)
|
||||||
|
|
||||||
return hostname === pattern || origin === pattern
|
return hostname === pattern || origin === pattern
|
||||||
})
|
})
|
||||||
} catch {
|
} catch {
|
||||||
@@ -84,14 +90,18 @@ async function bootstrap() {
|
|||||||
app.enableCors({
|
app.enableCors({
|
||||||
origin: (origin, callback) => {
|
origin: (origin, callback) => {
|
||||||
if (!origin) {
|
if (!origin) {
|
||||||
|
console.log('1')
|
||||||
callback(null, true)
|
callback(null, true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isOriginAllowed(origin)) {
|
if (isOriginAllowed(origin)) {
|
||||||
|
console.log('2')
|
||||||
|
|
||||||
callback(null, true)
|
callback(null, true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
console.log('3')
|
||||||
|
|
||||||
callback(new Error('Not allowed by CORS'), false)
|
callback(new Error('Not allowed by CORS'), false)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,12 +23,14 @@ export class GoodsService {
|
|||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
code: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
measure_unit: {
|
measure_unit: {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
code: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
category: {
|
category: {
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ export class AuthService {
|
|||||||
include: accountInclude,
|
include: accountInclude,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
console.log('account', account)
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
throw new NotFoundException('اطلاعات ورودی شما اشتباه است')
|
throw new NotFoundException('اطلاعات ورودی شما اشتباه است')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ export class BusinessActivitiesService {
|
|||||||
consumer_id,
|
consumer_id,
|
||||||
id,
|
id,
|
||||||
this.defaultSelect,
|
this.defaultSelect,
|
||||||
true,
|
|
||||||
)
|
)
|
||||||
return ResponseMapper.single(this.prepareBusinessActivityResponse(businessActivity))
|
return ResponseMapper.single(this.prepareBusinessActivityResponse(businessActivity))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { ApiPropertyOptional } from '@nestjs/swagger'
|
||||||
|
import { Type } from 'class-transformer'
|
||||||
|
import { IsOptional, Max, Min } from 'class-validator'
|
||||||
|
|
||||||
|
export class ConsumerSaleInvoicesFilterDto {
|
||||||
|
@ApiPropertyOptional({ type: Number, example: 1 })
|
||||||
|
@Type(() => Number)
|
||||||
|
@Min(1)
|
||||||
|
@IsOptional()
|
||||||
|
page?: number
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ type: Number, example: 10, description: 'Max value is 50.' })
|
||||||
|
@Type(() => Number)
|
||||||
|
@Min(1)
|
||||||
|
@Max(50)
|
||||||
|
@IsOptional()
|
||||||
|
perPage?: number
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import { PosInfo } from '@/common/decorators/posInfo.decorator'
|
import { PosInfo } from '@/common/decorators/posInfo.decorator'
|
||||||
import { TokenAccount } from '@/common/decorators/tokenInfo.decorator'
|
import { TokenAccount } from '@/common/decorators/tokenInfo.decorator'
|
||||||
import type { IPosPayload } from '@/common/models'
|
import type { IPosPayload } from '@/common/models'
|
||||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common'
|
import { Body, Controller, Get, Param, Post, Query } from '@nestjs/common'
|
||||||
import { ApiTags } from '@nestjs/swagger'
|
import { ApiTags } from '@nestjs/swagger'
|
||||||
|
import { ConsumerSaleInvoicesFilterDto } from './dto/saleInvoices-filter.dto'
|
||||||
import type {
|
import type {
|
||||||
SaleInvoicesServiceFindAllResponseDto,
|
SaleInvoicesServiceFindAllResponseDto,
|
||||||
SaleInvoicesServiceFindOneResponseDto,
|
SaleInvoicesServiceFindOneResponseDto,
|
||||||
@@ -18,8 +19,15 @@ export class StatisticsController {
|
|||||||
@Get('')
|
@Get('')
|
||||||
async findAll(
|
async findAll(
|
||||||
@TokenAccount('userId') consumerId: string,
|
@TokenAccount('userId') consumerId: string,
|
||||||
|
@Query('page') page = '1',
|
||||||
|
@Query('perPage') perPage = '10',
|
||||||
): Promise<SaleInvoicesServiceFindAllResponseDto> {
|
): Promise<SaleInvoicesServiceFindAllResponseDto> {
|
||||||
return this.service.findAll(consumerId)
|
const filter: ConsumerSaleInvoicesFilterDto = {
|
||||||
|
page: Number(page) || 1,
|
||||||
|
perPage: Number(perPage) || 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.service.findAll(consumerId, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { PrismaService } from '@/prisma/prisma.service'
|
|||||||
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'
|
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'
|
||||||
import { ResponseMapper } from 'common/response/response-mapper'
|
import { ResponseMapper } from 'common/response/response-mapper'
|
||||||
import { SalesInvoiceTspService } from '../../tspProviders/sales-invoice-tsp.service'
|
import { SalesInvoiceTspService } from '../../tspProviders/sales-invoice-tsp.service'
|
||||||
|
import { ConsumerSaleInvoicesFilterDto } from './dto/saleInvoices-filter.dto'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SaleInvoicesService {
|
export class SaleInvoicesService {
|
||||||
@@ -21,6 +22,9 @@ export class SaleInvoicesService {
|
|||||||
private sharedSaleInvoiceActionsService: SharedSaleInvoiceActionsService,
|
private sharedSaleInvoiceActionsService: SharedSaleInvoiceActionsService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
private readonly defaultPerPage = 10
|
||||||
|
private readonly maxPerPage = 50
|
||||||
|
|
||||||
private readonly defaultSelect: SalesInvoiceSelect = {
|
private readonly defaultSelect: SalesInvoiceSelect = {
|
||||||
pos: {
|
pos: {
|
||||||
select: {
|
select: {
|
||||||
@@ -54,27 +58,45 @@ export class SaleInvoicesService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAll(consumer_id: string, page = 1, perPage = 10) {
|
async findAll(consumer_id: string, filter: ConsumerSaleInvoicesFilterDto) {
|
||||||
const invoicesWhere: SalesInvoiceWhereInput = {
|
const invoicesWhere: SalesInvoiceWhereInput = {
|
||||||
consumer_account: {
|
consumer_account: {
|
||||||
consumer_id,
|
consumer_id,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
const normalizedPageValue = Number(filter.page ?? 1)
|
||||||
|
const normalizedPage = Number.isFinite(normalizedPageValue)
|
||||||
|
? Math.max(1, Math.floor(normalizedPageValue))
|
||||||
|
: 1
|
||||||
|
|
||||||
|
const requestedPerPageValue = Number(filter.perPage ?? this.defaultPerPage)
|
||||||
|
const requestedPerPage = Number.isFinite(requestedPerPageValue)
|
||||||
|
? Math.max(1, Math.floor(requestedPerPageValue))
|
||||||
|
: this.defaultPerPage
|
||||||
|
const normalizedPerPage = Math.min(requestedPerPage, this.maxPerPage)
|
||||||
|
|
||||||
const [invoices, total] = await this.prisma.$transaction(async tx => [
|
const [invoices, total] = await this.prisma.$transaction(async tx => [
|
||||||
await tx.salesInvoice.findMany({
|
await tx.salesInvoice.findMany({
|
||||||
where: invoicesWhere,
|
where: invoicesWhere,
|
||||||
|
orderBy: {
|
||||||
|
created_at: 'desc',
|
||||||
|
},
|
||||||
|
skip: (normalizedPage - 1) * normalizedPerPage,
|
||||||
|
take: normalizedPerPage,
|
||||||
select: {
|
select: {
|
||||||
...QUERY_CONSTANTS.SALE_INVOICE.summarySelect,
|
...QUERY_CONSTANTS.SALE_INVOICE.summarySelect,
|
||||||
...this.defaultSelect,
|
...this.defaultSelect,
|
||||||
},
|
},
|
||||||
skip: (page - 1) * perPage,
|
|
||||||
take: perPage,
|
|
||||||
}),
|
}),
|
||||||
await tx.salesInvoice.count({ where: invoicesWhere }),
|
await tx.salesInvoice.count({ where: invoicesWhere }),
|
||||||
])
|
])
|
||||||
|
|
||||||
const mappedInvoices = invoices.map(this.invoiceMapper)
|
const mappedInvoices = invoices.map(this.invoiceMapper)
|
||||||
return ResponseMapper.paginate(mappedInvoices, { page, perPage, total })
|
return ResponseMapper.paginate(mappedInvoices, {
|
||||||
|
page: normalizedPage,
|
||||||
|
perPage: normalizedPerPage,
|
||||||
|
total,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOne(consumer_id: string, id: string) {
|
async findOne(consumer_id: string, id: string) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export class GoodsService {
|
|||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
name: true,
|
name: true,
|
||||||
|
code: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
local_sku: true,
|
local_sku: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user