Compare commits
2 Commits
9aa12184a1
...
2c97b7302d
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c97b7302d | |||
| 2dc9480170 |
+2
-4
@@ -7,10 +7,9 @@ import { AuthModule } from './modules/auth/auth.module'
|
|||||||
import { CatalogModule } from './modules/catalog/catalog.module'
|
import { CatalogModule } from './modules/catalog/catalog.module'
|
||||||
import { ConsumerModule } from './modules/consumer/consumer.module'
|
import { ConsumerModule } from './modules/consumer/consumer.module'
|
||||||
import { EnumsModule } from './modules/enums/enums.module'
|
import { EnumsModule } from './modules/enums/enums.module'
|
||||||
|
import { PublicInvoicesModule } from './modules/invoices/invoices.module'
|
||||||
import { PartnerModule } from './modules/partners/partners.module'
|
import { PartnerModule } from './modules/partners/partners.module'
|
||||||
import { PosModule } from './modules/pos/pos.module'
|
import { PosModule } from './modules/pos/pos.module'
|
||||||
import { SalesInvoicePaymentsModule } from './modules/pos/sales-invoices/sales-invoice-payments/sales-invoice-payments.module'
|
|
||||||
import { TriggerLogsModule } from './modules/trigger-logs/trigger-logs.module'
|
|
||||||
import { UploaderModule } from './modules/uploader/uploader.module'
|
import { UploaderModule } from './modules/uploader/uploader.module'
|
||||||
import { PrismaModule } from './prisma/prisma.module'
|
import { PrismaModule } from './prisma/prisma.module'
|
||||||
import { RedisModule } from './redis/redis.module'
|
import { RedisModule } from './redis/redis.module'
|
||||||
@@ -26,8 +25,7 @@ import { RedisModule } from './redis/redis.module'
|
|||||||
ConsumerModule,
|
ConsumerModule,
|
||||||
PosModule,
|
PosModule,
|
||||||
PartnerModule,
|
PartnerModule,
|
||||||
SalesInvoicePaymentsModule,
|
PublicInvoicesModule,
|
||||||
TriggerLogsModule,
|
|
||||||
UploaderModule,
|
UploaderModule,
|
||||||
ApplicationModule,
|
ApplicationModule,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -98,14 +98,6 @@ export const select: SalesInvoiceSelect = {
|
|||||||
notes: true,
|
notes: true,
|
||||||
payload: true,
|
payload: true,
|
||||||
good_snapshot: true,
|
good_snapshot: true,
|
||||||
good: {
|
|
||||||
select: {
|
|
||||||
name: true,
|
|
||||||
barcode: true,
|
|
||||||
image_url: true,
|
|
||||||
category: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
payments: {
|
payments: {
|
||||||
|
|||||||
@@ -411,11 +411,7 @@ export class SharedSaleInvoiceCreateService {
|
|||||||
sku_vat: goodsById.get(item.good_id!)?.sku.VAT || null,
|
sku_vat: goodsById.get(item.good_id!)?.sku.VAT || null,
|
||||||
payload: item.payload ? JSON.parse(JSON.stringify(item.payload)) : undefined,
|
payload: item.payload ? JSON.parse(JSON.stringify(item.payload)) : undefined,
|
||||||
good_snapshot: item.good_id
|
good_snapshot: item.good_id
|
||||||
? JSON.parse(
|
? JSON.parse(JSON.stringify(goodsById.get(item.good_id) || null))
|
||||||
JSON.stringify({
|
|
||||||
good: goodsById.get(item.good_id) || null,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
: undefined,
|
: undefined,
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import dayjs, { Dayjs } from 'dayjs'
|
||||||
|
import 'dayjs/locale/fa'
|
||||||
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
|
import jalaliPlugin from 'jalaliday/dayjs'
|
||||||
|
|
||||||
|
dayjs.extend(jalaliPlugin)
|
||||||
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
|
export function toJalali(date: string | number | Date | Dayjs): Date {
|
||||||
|
return dayjs(date).calendar('jalali').toDate()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toGregorian(date: string | number | Date | Dayjs): Date {
|
||||||
|
return dayjs(date).calendar('gregory').toDate()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCurrentJalaliSeasonStart(
|
||||||
|
baseDate: string | number | Date | Dayjs = dayjs(),
|
||||||
|
): Date {
|
||||||
|
const date = dayjs(baseDate).calendar('jalali')
|
||||||
|
const month = date.month() + 1
|
||||||
|
const seasonStartMonth = Math.floor((month - 1) / 3) * 3 + 1
|
||||||
|
return date.month(seasonStartMonth - 1).startOf('month').startOf('day').toDate()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCurrentJalaliSeasonEnd(
|
||||||
|
baseDate: string | number | Date | Dayjs = dayjs(),
|
||||||
|
): Date {
|
||||||
|
return dayjs(getCurrentJalaliSeasonStart(baseDate))
|
||||||
|
.add(2, 'month')
|
||||||
|
.endOf('month')
|
||||||
|
.endOf('day')
|
||||||
|
.toDate()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCurrentGregorianSeasonStart(
|
||||||
|
baseDate: string | number | Date | Dayjs = dayjs(),
|
||||||
|
): Date {
|
||||||
|
const date = dayjs(baseDate).calendar('gregory')
|
||||||
|
const month = date.month() + 1
|
||||||
|
const seasonStartMonth = Math.floor((month - 1) / 3) * 3 + 1
|
||||||
|
return date.month(seasonStartMonth - 1).startOf('month').startOf('day').toDate()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCurrentGregorianSeasonEnd(
|
||||||
|
baseDate: string | number | Date | Dayjs = dayjs(),
|
||||||
|
): Date {
|
||||||
|
return dayjs(getCurrentGregorianSeasonStart(baseDate))
|
||||||
|
.add(2, 'month')
|
||||||
|
.endOf('month')
|
||||||
|
.endOf('day')
|
||||||
|
.toDate()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCurrentJalaliSeasonRange(
|
||||||
|
baseDate: string | number | Date | Dayjs = dayjs(),
|
||||||
|
): { start: Date; end: Date } {
|
||||||
|
return {
|
||||||
|
start: getCurrentJalaliSeasonStart(baseDate),
|
||||||
|
end: getCurrentJalaliSeasonEnd(baseDate),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCurrentGregorianSeasonRange(
|
||||||
|
baseDate: string | number | Date | Dayjs = dayjs(),
|
||||||
|
): { start: Date; end: Date } {
|
||||||
|
return {
|
||||||
|
start: getCurrentGregorianSeasonStart(baseDate),
|
||||||
|
end: getCurrentGregorianSeasonEnd(baseDate),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
toJalali,
|
||||||
|
toGregorian,
|
||||||
|
getCurrentJalaliSeasonStart,
|
||||||
|
getCurrentJalaliSeasonEnd,
|
||||||
|
getCurrentGregorianSeasonStart,
|
||||||
|
getCurrentGregorianSeasonEnd,
|
||||||
|
getCurrentJalaliSeasonRange,
|
||||||
|
getCurrentGregorianSeasonRange,
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
export * from './date-formatter.utils'
|
||||||
export * from './enum-translator.util'
|
export * from './enum-translator.util'
|
||||||
export * from './field-validator.util'
|
export * from './field-validator.util'
|
||||||
export * from './http-client.util'
|
export * from './http-client.util'
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { EnumKeyMaker } from './enum'
|
|||||||
import { GuildKeyMaker } from './guild'
|
import { GuildKeyMaker } from './guild'
|
||||||
import { PartnerKeyMaker } from './partner'
|
import { PartnerKeyMaker } from './partner'
|
||||||
import { PosKeyMaker } from './pos'
|
import { PosKeyMaker } from './pos'
|
||||||
|
import { PublicSaleInvoiceKeyMaker } from './publicSaleInvoice'
|
||||||
|
|
||||||
// Keep backward-compatible static methods while separating key builders by domain.
|
// Keep backward-compatible static methods while separating key builders by domain.
|
||||||
export class RedisKeyMaker extends PartnerKeyMaker {
|
export class RedisKeyMaker extends PartnerKeyMaker {
|
||||||
@@ -25,6 +26,8 @@ export class RedisKeyMaker extends PartnerKeyMaker {
|
|||||||
|
|
||||||
static enumsAll = EnumKeyMaker.enumsAll
|
static enumsAll = EnumKeyMaker.enumsAll
|
||||||
static enumsValues = EnumKeyMaker.enumsValues
|
static enumsValues = EnumKeyMaker.enumsValues
|
||||||
|
|
||||||
|
static publicSaleInvoice = PublicSaleInvoiceKeyMaker.invoice
|
||||||
}
|
}
|
||||||
|
|
||||||
export { ConsumerKeyMaker, EnumKeyMaker, GuildKeyMaker, PartnerKeyMaker, PosKeyMaker }
|
export { ConsumerKeyMaker, EnumKeyMaker, GuildKeyMaker, PartnerKeyMaker, PosKeyMaker }
|
||||||
|
|||||||
@@ -14,4 +14,50 @@ export class PosKeyMaker {
|
|||||||
static goodListByGuildPattern(guildId: string): string {
|
static goodListByGuildPattern(guildId: string): string {
|
||||||
return `pos:goods:list:guildId:${guildId}:ba:*`
|
return `pos:goods:list:guildId:${guildId}:ba:*`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static statisticInvoicesPattern(businessActivityId: string): string {
|
||||||
|
return `pos:statistics:invoices:ba:${businessActivityId}:*`
|
||||||
|
}
|
||||||
|
static statisticInvoices(
|
||||||
|
businessActivityId: string,
|
||||||
|
posId: string,
|
||||||
|
from: string,
|
||||||
|
): string {
|
||||||
|
return `pos:statistics:invoices:ba:${businessActivityId}:pos:${posId}:from:${from}:all`
|
||||||
|
}
|
||||||
|
static statisticInvoicesNotSent(
|
||||||
|
businessActivityId: string,
|
||||||
|
posId: string,
|
||||||
|
from: string,
|
||||||
|
): string {
|
||||||
|
return `pos:statistics:invoices:ba:${businessActivityId}:pos:${posId}:from:${from}:not-sent`
|
||||||
|
}
|
||||||
|
static statisticInvoicesSuccess(
|
||||||
|
businessActivityId: string,
|
||||||
|
posId: string,
|
||||||
|
from: string,
|
||||||
|
): string {
|
||||||
|
return `pos:statistics:invoices:ba:${businessActivityId}:pos:${posId}:from:${from}:success`
|
||||||
|
}
|
||||||
|
static statisticInvoicesFailure(
|
||||||
|
businessActivityId: string,
|
||||||
|
posId: string,
|
||||||
|
from: string,
|
||||||
|
): string {
|
||||||
|
return `pos:statistics:invoices:ba:${businessActivityId}:pos:${posId}:from:${from}:failure`
|
||||||
|
}
|
||||||
|
static statisticInvoicesPending(
|
||||||
|
businessActivityId: string,
|
||||||
|
posId: string,
|
||||||
|
from: string,
|
||||||
|
): string {
|
||||||
|
return `pos:statistics:invoices:ba:${businessActivityId}:pos:${posId}:from:${from}:pending`
|
||||||
|
}
|
||||||
|
static statisticInvoicesCredit(
|
||||||
|
businessActivityId: string,
|
||||||
|
posId: string,
|
||||||
|
from: string,
|
||||||
|
): string {
|
||||||
|
return `pos:statistics:invoices:ba:${businessActivityId}:pos:${posId}:from:${from}:credit`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export class PublicSaleInvoiceKeyMaker {
|
||||||
|
static invoice(id: string): string {
|
||||||
|
return `publicSaleInvoices:${id}`
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { Public } from '@/common/decorators/public.decorator'
|
||||||
|
import { Controller, Get, Param } from '@nestjs/common'
|
||||||
|
import { ApiTags } from '@nestjs/swagger'
|
||||||
|
import { InvoicesService } from './invoices.service'
|
||||||
|
|
||||||
|
@ApiTags('PublicInvoices')
|
||||||
|
@Controller('public-invoices')
|
||||||
|
export class InvoicesController {
|
||||||
|
constructor(private readonly service: InvoicesService) {}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
@Public()
|
||||||
|
findOne(@Param('id') id: string) {
|
||||||
|
return this.service.findOne(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { PrismaService } from '@/prisma/prisma.service'
|
||||||
|
import { Module } from '@nestjs/common'
|
||||||
|
import { InvoicesController } from './invoices.controller'
|
||||||
|
import { InvoicesService } from './invoices.service'
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [InvoicesController],
|
||||||
|
providers: [InvoicesService, PrismaService],
|
||||||
|
})
|
||||||
|
export class PublicInvoicesModule {}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
import { RedisKeyMaker, translateEnumValue } from '@/common/utils'
|
||||||
|
import { TspProviderResponseStatus } from '@/generated/prisma/enums'
|
||||||
|
import { SalesInvoiceSelect } from '@/generated/prisma/models'
|
||||||
|
import { PrismaService } from '@/prisma/prisma.service'
|
||||||
|
import { RedisService } from '@/redis/redis.service'
|
||||||
|
import { Injectable } from '@nestjs/common'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class InvoicesService {
|
||||||
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly redisService: RedisService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private readonly summarySelect: SalesInvoiceSelect = {
|
||||||
|
id: true,
|
||||||
|
code: true,
|
||||||
|
invoice_date: true,
|
||||||
|
invoice_number: true,
|
||||||
|
notes: true,
|
||||||
|
tax_id: true,
|
||||||
|
total_amount: true,
|
||||||
|
type: true,
|
||||||
|
pos: {
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
complex: {
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
branch_code: true,
|
||||||
|
business_activity: {
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
guild: {
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
payments: {
|
||||||
|
select: {
|
||||||
|
amount: true,
|
||||||
|
paid_at: true,
|
||||||
|
payment_method: true,
|
||||||
|
terminal_info: {
|
||||||
|
select: {
|
||||||
|
stan: true,
|
||||||
|
rrn: true,
|
||||||
|
transaction_date_time: true,
|
||||||
|
customer_card_no: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unknown_customer: true,
|
||||||
|
customer: {
|
||||||
|
select: {
|
||||||
|
type: true,
|
||||||
|
legal: {
|
||||||
|
select: {
|
||||||
|
name: true,
|
||||||
|
economic_code: true,
|
||||||
|
postal_code: true,
|
||||||
|
registration_number: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
individual: {
|
||||||
|
select: {
|
||||||
|
economic_code: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
mobile_number: true,
|
||||||
|
national_id: true,
|
||||||
|
postal_code: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
select: {
|
||||||
|
good_snapshot: true,
|
||||||
|
measure_unit_text: true,
|
||||||
|
notes: true,
|
||||||
|
quantity: true,
|
||||||
|
total_amount: true,
|
||||||
|
unit_price: true,
|
||||||
|
discount: true,
|
||||||
|
payload: true,
|
||||||
|
sku_code: true,
|
||||||
|
sku_vat: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly select: SalesInvoiceSelect = {
|
||||||
|
...this.summarySelect,
|
||||||
|
tsp_attempts: {
|
||||||
|
orderBy: {
|
||||||
|
created_at: 'desc',
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
status: true,
|
||||||
|
},
|
||||||
|
take: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly where = () => ({})
|
||||||
|
|
||||||
|
private readonly invoiceMapper = (invoice: any) => {
|
||||||
|
const { tsp_attempts, type, ...rest } = invoice || {}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...rest,
|
||||||
|
type: translateEnumValue('TspProviderRequestType', type),
|
||||||
|
status: translateEnumValue(
|
||||||
|
'TspProviderResponseStatus',
|
||||||
|
invoice.tsp_attempts?.[0]?.status || TspProviderResponseStatus.NOT_SEND,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOne(id: string) {
|
||||||
|
await this.redisService.delete(RedisKeyMaker.publicSaleInvoice(id))
|
||||||
|
return await this.redisService.getAndSet(
|
||||||
|
RedisKeyMaker.publicSaleInvoice(id),
|
||||||
|
'single',
|
||||||
|
async () => {
|
||||||
|
const item = await this.prisma.salesInvoice.findUnique({
|
||||||
|
where: { ...this.where(), id },
|
||||||
|
select: this.select,
|
||||||
|
})
|
||||||
|
return this.invoiceMapper(item)
|
||||||
|
},
|
||||||
|
60 * 60,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,4 +16,72 @@ export class PosCacheInvalidationService {
|
|||||||
async invalidatePosGoodsByGuildPattern(guildId: string): Promise<void> {
|
async invalidatePosGoodsByGuildPattern(guildId: string): Promise<void> {
|
||||||
this.redisService.deleteByPattern(PosKeyMaker.goodListByGuildPattern(guildId))
|
this.redisService.deleteByPattern(PosKeyMaker.goodListByGuildPattern(guildId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async invalidatePosStatisticsInvoicesByBusinessActivityId(
|
||||||
|
businessActivityId: string,
|
||||||
|
): Promise<void> {
|
||||||
|
this.redisService.deleteByPattern(
|
||||||
|
PosKeyMaker.statisticInvoicesPattern(businessActivityId),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async invalidatePosStatisticsInvoicesAll(
|
||||||
|
businessActivityId: string,
|
||||||
|
posId: string,
|
||||||
|
from: string,
|
||||||
|
): Promise<void> {
|
||||||
|
this.redisService.delete(
|
||||||
|
PosKeyMaker.statisticInvoices(businessActivityId, posId, from),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async invalidatePosStatisticsInvoicesFailure(
|
||||||
|
businessActivityId: string,
|
||||||
|
posId: string,
|
||||||
|
from: string,
|
||||||
|
): Promise<void> {
|
||||||
|
this.redisService.delete(
|
||||||
|
PosKeyMaker.statisticInvoicesFailure(businessActivityId, posId, from),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async invalidatePosStatisticsInvoicesSuccess(
|
||||||
|
businessActivityId: string,
|
||||||
|
posId: string,
|
||||||
|
from: string,
|
||||||
|
): Promise<void> {
|
||||||
|
this.redisService.delete(
|
||||||
|
PosKeyMaker.statisticInvoicesSuccess(businessActivityId, posId, from),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async invalidatePosStatisticsInvoicesPending(
|
||||||
|
businessActivityId: string,
|
||||||
|
posId: string,
|
||||||
|
from: string,
|
||||||
|
): Promise<void> {
|
||||||
|
this.redisService.delete(
|
||||||
|
PosKeyMaker.statisticInvoicesPending(businessActivityId, posId, from),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async invalidatePosStatisticsInvoicesCredit(
|
||||||
|
businessActivityId: string,
|
||||||
|
posId: string,
|
||||||
|
from: string,
|
||||||
|
): Promise<void> {
|
||||||
|
this.redisService.delete(
|
||||||
|
PosKeyMaker.statisticInvoicesCredit(businessActivityId, posId, from),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async invalidatePosStatisticsInvoicesNotSent(
|
||||||
|
businessActivityId: string,
|
||||||
|
posId: string,
|
||||||
|
from: string,
|
||||||
|
): Promise<void> {
|
||||||
|
this.redisService.delete(
|
||||||
|
PosKeyMaker.statisticInvoicesNotSent(businessActivityId, posId, from),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { PosController } from './pos.controller'
|
|||||||
import { PosMiddleware } from './pos.middleware'
|
import { PosMiddleware } from './pos.middleware'
|
||||||
import { PosService } from './pos.service'
|
import { PosService } from './pos.service'
|
||||||
import { PosSalesInvoicesModule } from './sales-invoices/sales-invoices.module'
|
import { PosSalesInvoicesModule } from './sales-invoices/sales-invoices.module'
|
||||||
|
import { StatisticsModule } from './statistics/statistics.module'
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
controllers: [PosController],
|
controllers: [PosController],
|
||||||
@@ -18,6 +19,7 @@ import { PosSalesInvoicesModule } from './sales-invoices/sales-invoices.module'
|
|||||||
PosGoodsModule,
|
PosGoodsModule,
|
||||||
PosSalesInvoicesModule,
|
PosSalesInvoicesModule,
|
||||||
OwnedGoodsModule,
|
OwnedGoodsModule,
|
||||||
|
StatisticsModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class PosModule implements NestModule {
|
export class PosModule implements NestModule {
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { PosInfo } from '@/common/decorators/posInfo.decorator'
|
||||||
|
import type { IPosPayload } from '@/common/models'
|
||||||
|
import { Controller, Get, Query } from '@nestjs/common'
|
||||||
|
import { ApiTags } from '@nestjs/swagger'
|
||||||
|
import { StatisticsService } from './statistics.service'
|
||||||
|
|
||||||
|
@ApiTags('PosStatistics')
|
||||||
|
@Controller('pos/statistics')
|
||||||
|
export class StatisticsController {
|
||||||
|
constructor(private readonly service: StatisticsService) {}
|
||||||
|
|
||||||
|
@Get('sale-invoices')
|
||||||
|
findAll(@PosInfo() PosInfo: IPosPayload, @Query('from_date') from_date?: string) {
|
||||||
|
return this.service.findAll(
|
||||||
|
PosInfo.pos_id,
|
||||||
|
PosInfo.business_id,
|
||||||
|
from_date ? new Date(from_date) : new Date(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { StatisticsService } from './statistics.service';
|
||||||
|
import { StatisticsController } from './statistics.controller';
|
||||||
|
import { PrismaService } from '@/prisma/prisma.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [StatisticsController],
|
||||||
|
providers: [StatisticsService, PrismaService],
|
||||||
|
})
|
||||||
|
export class StatisticsModule {}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import { ResponseMapper } from '@/common/response/response-mapper'
|
||||||
|
import { getCurrentJalaliSeasonRange } from '@/common/utils'
|
||||||
|
import { TspProviderResponseStatus } from '@/generated/prisma/enums'
|
||||||
|
import { PrismaService } from '@/prisma/prisma.service'
|
||||||
|
import { Injectable } from '@nestjs/common'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class StatisticsService {
|
||||||
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async findAll(posId: string, businessId: string, fromDate: Date = new Date()) {
|
||||||
|
const seasonDate = getCurrentJalaliSeasonRange(fromDate)
|
||||||
|
|
||||||
|
const [item] = await this.prisma.$queryRaw<
|
||||||
|
Array<{
|
||||||
|
all_amount_sum: number | null
|
||||||
|
all_count: number | null
|
||||||
|
success_amount_sum: number | null
|
||||||
|
success_count: number | null
|
||||||
|
failure_amount_sum: number | null
|
||||||
|
failure_count: number | null
|
||||||
|
pending_amount_sum: number | null
|
||||||
|
pending_count: number | null
|
||||||
|
not_sended_amount_sum: number | null
|
||||||
|
not_sended_count: number | null
|
||||||
|
}>
|
||||||
|
>`
|
||||||
|
SELECT
|
||||||
|
SUM(si.total_amount) AS all_amount_sum,
|
||||||
|
COUNT(*) AS all_count,
|
||||||
|
SUM(CASE WHEN la.status = ${TspProviderResponseStatus.SUCCESS} THEN si.total_amount ELSE 0 END) AS success_amount_sum,
|
||||||
|
SUM(CASE WHEN la.status = ${TspProviderResponseStatus.SUCCESS} THEN 1 ELSE 0 END) AS success_count,
|
||||||
|
SUM(CASE WHEN la.status = ${TspProviderResponseStatus.FAILURE} THEN si.total_amount ELSE 0 END) AS failure_amount_sum,
|
||||||
|
SUM(CASE WHEN la.status = ${TspProviderResponseStatus.FAILURE} THEN 1 ELSE 0 END) AS failure_count,
|
||||||
|
SUM(CASE WHEN la.status = ${TspProviderResponseStatus.QUEUED} THEN si.total_amount ELSE 0 END) AS pending_amount_sum,
|
||||||
|
SUM(CASE WHEN la.status = ${TspProviderResponseStatus.QUEUED} THEN 1 ELSE 0 END) AS pending_count,
|
||||||
|
SUM(CASE WHEN la.status IS NULL THEN si.total_amount ELSE 0 END) AS not_sended_amount_sum,
|
||||||
|
SUM(CASE WHEN la.status IS NULL THEN 1 ELSE 0 END) AS not_sended_count
|
||||||
|
FROM sales_invoices si
|
||||||
|
INNER JOIN poses p ON p.id = si.pos_id
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT a1.invoice_id, a1.status
|
||||||
|
FROM sale_invoice_tsp_attempts a1
|
||||||
|
INNER JOIN (
|
||||||
|
SELECT invoice_id, MAX(attempt_no) AS max_attempt_no
|
||||||
|
FROM sale_invoice_tsp_attempts
|
||||||
|
GROUP BY invoice_id
|
||||||
|
) latest
|
||||||
|
ON latest.invoice_id = a1.invoice_id
|
||||||
|
AND latest.max_attempt_no = a1.attempt_no
|
||||||
|
) la ON la.invoice_id = si.id
|
||||||
|
WHERE si.invoice_date >= ${seasonDate.start}
|
||||||
|
AND si.invoice_date <= ${seasonDate.end}
|
||||||
|
AND si.pos_id = ${posId}
|
||||||
|
AND p.complex_id IN (
|
||||||
|
SELECT c.id FROM complexes c WHERE c.business_activity_id = ${businessId}
|
||||||
|
)
|
||||||
|
`
|
||||||
|
|
||||||
|
return ResponseMapper.single({
|
||||||
|
all: {
|
||||||
|
total_amount: Number(item?.all_amount_sum || 0),
|
||||||
|
total_tax: Number(0),
|
||||||
|
count: Number(item?.all_count || 0),
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
total_amount: Number(item?.success_amount_sum || 0),
|
||||||
|
total_tax: Number(0),
|
||||||
|
count: Number(item?.success_count || 0),
|
||||||
|
},
|
||||||
|
failure: {
|
||||||
|
total_amount: Number(item?.failure_amount_sum || 0),
|
||||||
|
total_tax: Number(0),
|
||||||
|
count: Number(item?.failure_count || 0),
|
||||||
|
},
|
||||||
|
pending: {
|
||||||
|
total_amount: Number(item?.pending_amount_sum || 0),
|
||||||
|
total_tax: Number(0),
|
||||||
|
count: Number(item?.pending_count || 0),
|
||||||
|
},
|
||||||
|
notSended: {
|
||||||
|
total_amount: Number(item?.not_sended_amount_sum || 0),
|
||||||
|
total_tax: Number(0),
|
||||||
|
count: Number(item?.not_sended_count || 0),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { ApiProperty, PartialType } from '@nestjs/swagger'
|
|
||||||
import { IsString } from 'class-validator'
|
|
||||||
|
|
||||||
export class CreateTriggerLogDto {
|
|
||||||
@ApiProperty()
|
|
||||||
@IsString()
|
|
||||||
message: string
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@IsString()
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UpdateTriggerLogDto extends PartialType(CreateTriggerLogDto) {}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import type { TriggerLogsService } from '../trigger-logs.service'
|
|
||||||
|
|
||||||
export type TriggerLogsServiceCreateResponseDto = Awaited<ReturnType<TriggerLogsService['create']>>
|
|
||||||
export type TriggerLogsServiceFindAllResponseDto = Awaited<ReturnType<TriggerLogsService['findAll']>>
|
|
||||||
export type TriggerLogsServiceFindOneResponseDto = Awaited<ReturnType<TriggerLogsService['findOne']>>
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { Body, Controller, Get, Param, Post } from '@nestjs/common'
|
|
||||||
import { TriggerLogsService } from './trigger-logs.service'
|
|
||||||
|
|
||||||
@Controller('trigger-logs')
|
|
||||||
export class TriggerLogsController {
|
|
||||||
constructor(private readonly triggerLogsService: TriggerLogsService) {}
|
|
||||||
|
|
||||||
@Get()
|
|
||||||
findAll() {
|
|
||||||
return this.triggerLogsService.findAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get(':id')
|
|
||||||
findOne(@Param('id') id: string) {
|
|
||||||
return this.triggerLogsService.findOne(+id)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Post()
|
|
||||||
create(@Body() data: any) {
|
|
||||||
return this.triggerLogsService.create(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { Module } from '@nestjs/common'
|
|
||||||
import { TriggerLogsController } from './trigger-logs.controller'
|
|
||||||
import { TriggerLogsService } from './trigger-logs.service'
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
controllers: [TriggerLogsController],
|
|
||||||
providers: [TriggerLogsService],
|
|
||||||
})
|
|
||||||
export class TriggerLogsModule {}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { Injectable } from '@nestjs/common'
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class TriggerLogsService {
|
|
||||||
findAll() {
|
|
||||||
// TODO: Implement fetching all trigger logs
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
findOne(id: number) {
|
|
||||||
// TODO: Implement fetching a single trigger log
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
create(data: any) {
|
|
||||||
// TODO: Implement trigger log creation
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user