feat: add stock keeping units management

- Created migration to drop `type` column from `stock_keeping_units` table.
- Added `Guild` model to Prisma schema.
- Implemented `BusinessActivitiesQueryService` for querying business activities.
- Developed `GoodsSharedService` for handling goods creation and updates.
- Introduced DTOs for creating and updating stock keeping units.
- Created controller and service for managing stock keeping units.
- Implemented response DTOs for stock keeping units service.
- Established module for stock keeping units management.
This commit is contained in:
2026-05-07 20:30:24 +03:30
parent 658496320b
commit fbe7230865
77 changed files with 2065 additions and 1846 deletions
-2
View File
@@ -9,7 +9,6 @@ import { AdminGuildsModule } from './guilds/guilds.module'
import { AdminLicensesModule } from './licenses/licenses.module'
import { AdminPartnersModule } from './partners/partners.module'
import { AdminProvidersModule } from './providers/providers.module'
import { AdminStockKeepingUnitsModule } from './stock-keeping-units/stock-keeping-units.module'
import { AdminTranslateModule } from './translate/translate.module'
import { AdminUserAccountsModule } from './users/accounts/accounts.module'
import { AdminUsersModule } from './users/users.module'
@@ -26,7 +25,6 @@ import { AdminUsersModule } from './users/users.module'
AdminGuildsModule,
AdminDeviceBrandsModule,
AdminDevicesModule,
AdminStockKeepingUnitsModule,
AdminLicensesModule,
AdminConsumersModule,
],
@@ -1,3 +1,4 @@
import { BusinessActivitiesQueryService } from '@/common/services/businessActivities/business-activities-query.service'
import { PrismaModule } from '@/prisma/prisma.module'
import { Module } from '@nestjs/common'
import { BusinessActivitiesController } from './business-activities.controller'
@@ -7,6 +8,6 @@ import { AdminBusinessActivityComplexesModule } from './complexes/complexes.modu
@Module({
imports: [PrismaModule, AdminBusinessActivityComplexesModule],
controllers: [BusinessActivitiesController],
providers: [BusinessActivitiesService],
providers: [BusinessActivitiesService, BusinessActivitiesQueryService],
})
export class AdminConsumerBusinessActivitiesModule {}
@@ -1,43 +1,35 @@
import { BusinessActivitiesQueryService } from '@/common/services/businessActivities/business-activities-query.service'
import { BusinessActivitySelect } from '@/generated/prisma/models'
import { PrismaService } from '@/prisma/prisma.service'
import { Injectable } from '@nestjs/common'
import { ResponseMapper } from 'common/response/response-mapper'
@Injectable()
export class BusinessActivitiesService {
constructor(private readonly prisma: PrismaService) {}
defaultSelect: BusinessActivitySelect
defaultSelect = {
guild: {
select: {
id: true,
code: true,
name: true,
},
},
id: true,
name: true,
economic_code: true,
fiscal_id: true,
partner_token: true,
created_at: true,
} as BusinessActivitySelect
constructor(
private readonly businessActivitiesQueryService: BusinessActivitiesQueryService,
) {
this.defaultSelect = {
...this.businessActivitiesQueryService.baseSelect,
} as BusinessActivitySelect
}
async findAll(consumer_id: string) {
const businessActivities = await this.prisma.businessActivity.findMany({
where: {
const businessActivities =
await this.businessActivitiesQueryService.findAllByConsumer(
consumer_id,
},
select: this.defaultSelect,
})
this.defaultSelect,
)
return ResponseMapper.list(businessActivities)
}
async findOne(consumer_id: string, id: string) {
const businessActivity = await this.prisma.businessActivity.findUnique({
where: { consumer_id, id },
select: this.defaultSelect,
})
const businessActivity = await this.businessActivitiesQueryService.findOneByConsumer(
consumer_id,
id,
this.defaultSelect,
)
return ResponseMapper.single(businessActivity)
}
@@ -1,6 +1,5 @@
import { LicenseStatus } from '@/generated/prisma/enums'
import { ApiProperty, PartialType } from '@nestjs/swagger'
import { IsDateString, IsEnum } from 'class-validator'
import { IsDateString } from 'class-validator'
export class CreateLicenseDto {
@ApiProperty({ required: true, default: new Date().toISOString() })
@@ -24,8 +23,4 @@ export class CreateLicenseDto {
partner_id: string
}
export class UpdateLicenseDto extends PartialType(CreateLicenseDto) {
@ApiProperty({ enum: LicenseStatus })
@IsEnum(LicenseStatus)
status?: LicenseStatus
}
export class UpdateLicenseDto extends PartialType(CreateLicenseDto) {}
@@ -29,7 +29,7 @@ export class GoodsController {
}
@Post()
@UseInterceptors(FileInterceptor('file', multerImageOptions))
@UseInterceptors(FileInterceptor('image', multerImageOptions))
@ApiConsumes('multipart/form-data')
@ApiBody({
schema: {
+7 -1
View File
@@ -4,9 +4,15 @@ import { GuildGoodCategoriesModule } from './good-categories/good-categories.mod
import { GuildGoodsModule } from './goods/goods.module'
import { GuildsController } from './guilds.controller'
import { GuildsService } from './guilds.service'
import { AdminStockKeepingUnitsModule } from './stock-keeping-units/stock-keeping-units.module'
@Module({
imports: [PrismaModule, GuildGoodsModule, GuildGoodCategoriesModule],
imports: [
PrismaModule,
GuildGoodsModule,
GuildGoodCategoriesModule,
AdminStockKeepingUnitsModule,
],
providers: [GuildsService],
controllers: [GuildsController],
})
+14 -11
View File
@@ -1,4 +1,5 @@
import { ResponseMapper } from '@/common/response/response-mapper'
import { translateEnumValue } from '@/common/utils'
import { PrismaService } from '@/prisma/prisma.service'
import { Injectable } from '@nestjs/common'
import { CreateGuildDto } from './dto/create-guild.dto'
@@ -8,6 +9,15 @@ import { UpdateGuildDto } from './dto/update-guild.dto'
export class GuildsService {
constructor(private prisma: PrismaService) {}
private readonly dataMapper = (guild: any) => {
const { _count, invoice_template, ...rest } = guild
return {
...rest,
businessActivitiesCount: guild._count.business_activities,
invoice_template: translateEnumValue('InvoiceTemplateType', guild.invoice_template),
}
}
async findAll() {
const [guilds, total] = await this.prisma.$transaction([
this.prisma.guild.findMany({
@@ -22,16 +32,7 @@ export class GuildsService {
this.prisma.guild.count(),
])
return ResponseMapper.paginate(
guilds.map(guild => {
const { _count, ...rest } = guild
return {
...rest,
businessActivitiesCount: guild._count.business_activities,
}
}),
{ total },
)
return ResponseMapper.paginate(guilds.map(this.dataMapper), { total })
}
async findOne(id: string) {
@@ -46,7 +47,9 @@ export class GuildsService {
},
})
return ResponseMapper.single(guild)
const mappedData = this.dataMapper(guild)
return ResponseMapper.single(mappedData)
}
async create(data: CreateGuildDto) {
@@ -1,36 +1,40 @@
import { Body, Controller, Get, Param, Patch, Post } from '@nestjs/common'
import { ApiTags } from '@nestjs/swagger'
import { CreateStockKeepingUnitDto } from './dto/create-stock-keeping-unit.dto'
import type {
StockKeepingUnitsServiceCreateResponseDto,
StockKeepingUnitsServiceFindAllResponseDto,
StockKeepingUnitsServiceUpdateResponseDto,
} from './dto/stock-keeping-units-response.dto'
import { CreateStockKeepingUnitDto } from './dto/create-stock-keeping-unit.dto'
import { UpdateStockKeepingUnitDto } from './dto/update-stock-keeping-unit.dto'
import { StockKeepingUnitsService } from './stock-keeping-units.service'
@ApiTags('AdminStockKeepingUnits')
@Controller('admin/stock-keeping-units')
@Controller('admin/guilds/:guildId/stock-keeping-units')
export class StockKeepingUnitsController {
constructor(private readonly service: StockKeepingUnitsService) {}
@Get()
async findAll(): Promise<StockKeepingUnitsServiceFindAllResponseDto> {
return this.service.findAll()
async findAll(
@Param('guildId') guildId: string,
): Promise<StockKeepingUnitsServiceFindAllResponseDto> {
return this.service.findAll(guildId)
}
@Post()
async create(
@Param('guildId') guildId: string,
@Body() data: CreateStockKeepingUnitDto,
): Promise<StockKeepingUnitsServiceCreateResponseDto> {
return this.service.create(data)
return this.service.create(guildId, data)
}
@Patch(':id')
async update(
@Param('guildId') guildId: string,
@Param('id') id: string,
@Body() data: UpdateStockKeepingUnitDto,
): Promise<StockKeepingUnitsServiceUpdateResponseDto> {
return this.service.update(id, data)
return this.service.update(guildId, id, data)
}
}
@@ -0,0 +1,53 @@
import { PrismaService } from '@/prisma/prisma.service'
import { Injectable } from '@nestjs/common'
import { ResponseMapper } from 'common/response/response-mapper'
import { CreateStockKeepingUnitDto } from './dto/create-stock-keeping-unit.dto'
import { UpdateStockKeepingUnitDto } from './dto/update-stock-keeping-unit.dto'
@Injectable()
export class StockKeepingUnitsService {
constructor(private readonly prisma: PrismaService) {}
private mapData(data: any) {
const { name, is_foreign, is_domestic, ...rest } = data
return {
...rest,
name: `${name} ${is_domestic ? 'اختصاصی' : 'عمومی'} ${is_foreign ? 'خارجی' : 'داخلی'}`,
}
}
async findAll(guild_id: string) {
const items = await this.prisma.stockKeepingUnits.findMany({
where: { guild_id },
orderBy: { created_at: 'desc' },
})
const mappedData = items.map(this.mapData)
return ResponseMapper.list(mappedData)
}
async create(guild_id: string, data: CreateStockKeepingUnitDto) {
const item = await this.prisma.stockKeepingUnits.create({
data: {
...data,
guild: {
connect: {
id: guild_id,
},
},
},
})
const mappedData = this.mapData(item)
return ResponseMapper.create(mappedData)
}
async update(guild_id: string, id: string, data: UpdateStockKeepingUnitDto) {
const item = await this.prisma.stockKeepingUnits.update({
where: { guild_id, id },
data,
})
return ResponseMapper.update(item)
}
}
@@ -1,6 +1,5 @@
import { ApiProperty } from '@nestjs/swagger'
import { IsDateString, IsEnum, IsOptional, IsString } from 'class-validator'
import { LicenseStatus } from 'generated/prisma/enums'
import { IsDateString, IsOptional, IsString } from 'class-validator'
export class CreateLicenseDto {
@IsString()
@@ -35,7 +34,4 @@ export class UpdateLicenseDto {
@IsDateString()
expires_at?: string
@IsEnum(LicenseStatus)
status?: LicenseStatus
}
@@ -1,30 +0,0 @@
import { PrismaService } from '@/prisma/prisma.service'
import { Injectable } from '@nestjs/common'
import { ResponseMapper } from 'common/response/response-mapper'
import { CreateStockKeepingUnitDto } from './dto/create-stock-keeping-unit.dto'
import { UpdateStockKeepingUnitDto } from './dto/update-stock-keeping-unit.dto'
@Injectable()
export class StockKeepingUnitsService {
constructor(private readonly prisma: PrismaService) {}
async findAll() {
const items = await this.prisma.stockKeepingUnits.findMany({
orderBy: { created_at: 'desc' },
})
return ResponseMapper.list(items)
}
async create(data: CreateStockKeepingUnitDto) {
const item = await this.prisma.stockKeepingUnits.create({ data })
return ResponseMapper.create(item)
}
async update(id: string, data: UpdateStockKeepingUnitDto) {
const item = await this.prisma.stockKeepingUnits.update({
where: { id },
data,
})
return ResponseMapper.update(item)
}
}