fix: scope external barcode print records
This commit is contained in:
parent
076075a3a8
commit
c58f642574
@ -2,6 +2,7 @@ import { Prisma, type BarcodePrintImportRecord } from '@prisma/client'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
export type BarcodePrintImportScope = {
|
||||
createdBy?: string
|
||||
normalizedSupplierName?: string
|
||||
supplierCode?: string
|
||||
}
|
||||
@ -33,13 +34,18 @@ function readText(value: string | undefined) {
|
||||
}
|
||||
|
||||
function normalizedSupplierScope(scope: BarcodePrintImportScope) {
|
||||
const conditions: Array<Record<string, string>> = []
|
||||
if (scope.createdBy) {
|
||||
conditions.push({ createdBy: scope.createdBy })
|
||||
}
|
||||
if (scope.normalizedSupplierName) {
|
||||
return { normalizedSupplierName: scope.normalizedSupplierName }
|
||||
conditions.push({ normalizedSupplierName: scope.normalizedSupplierName })
|
||||
} else if (scope.supplierCode) {
|
||||
conditions.push({ supplierCode: scope.supplierCode })
|
||||
}
|
||||
if (scope.supplierCode) {
|
||||
return { supplierCode: scope.supplierCode }
|
||||
}
|
||||
return {}
|
||||
if (conditions.length === 0) return {}
|
||||
if (conditions.length === 1) return conditions[0]
|
||||
return { OR: conditions }
|
||||
}
|
||||
|
||||
function sqlColumn(alias: string, column: string) {
|
||||
@ -63,13 +69,20 @@ function equalsCondition(column: Prisma.Sql, value: string | undefined) {
|
||||
}
|
||||
|
||||
function buildScopeSql(scope: BarcodePrintImportScope, alias: string) {
|
||||
const conditions: Prisma.Sql[] = []
|
||||
if (scope.createdBy) {
|
||||
conditions.push(Prisma.sql`${sqlColumn(alias, 'createdBy')} = ${scope.createdBy}`)
|
||||
}
|
||||
if (scope.normalizedSupplierName) {
|
||||
return Prisma.sql` AND ${sqlColumn(alias, 'normalizedSupplierName')} = ${scope.normalizedSupplierName}`
|
||||
conditions.push(
|
||||
Prisma.sql`${sqlColumn(alias, 'normalizedSupplierName')} = ${scope.normalizedSupplierName}`,
|
||||
)
|
||||
} else if (scope.supplierCode) {
|
||||
conditions.push(Prisma.sql`${sqlColumn(alias, 'supplierCode')} = ${scope.supplierCode}`)
|
||||
}
|
||||
if (scope.supplierCode) {
|
||||
return Prisma.sql` AND ${sqlColumn(alias, 'supplierCode')} = ${scope.supplierCode}`
|
||||
}
|
||||
return Prisma.empty
|
||||
if (conditions.length === 0) return Prisma.empty
|
||||
if (conditions.length === 1) return Prisma.sql` AND ${conditions[0]}`
|
||||
return Prisma.sql` AND (${Prisma.join(conditions, ' OR ')})`
|
||||
}
|
||||
|
||||
function buildBarcodeFilterSql(filters: BarcodePrintImportListFilters) {
|
||||
@ -227,7 +240,11 @@ class BarcodePrintImportRepositoryClass {
|
||||
take: number
|
||||
}): Promise<BarcodePrintImportPage> {
|
||||
// A supplier may only see brand data attached to its own barcode records.
|
||||
if (params.scope.normalizedSupplierName || params.scope.supplierCode) {
|
||||
if (
|
||||
params.scope.createdBy ||
|
||||
params.scope.normalizedSupplierName ||
|
||||
params.scope.supplierCode
|
||||
) {
|
||||
return { records: [], hasNext: false }
|
||||
}
|
||||
|
||||
@ -277,7 +294,7 @@ class BarcodePrintImportRepositoryClass {
|
||||
filters: BarcodePrintImportListFilters,
|
||||
) {
|
||||
// Brand-only records are never in a supplier's independent list.
|
||||
if (scope.normalizedSupplierName || scope.supplierCode) return 0
|
||||
if (scope.createdBy || scope.normalizedSupplierName || scope.supplierCode) return 0
|
||||
if (!supportsStandaloneBrandFilters(filters)) return 0
|
||||
|
||||
const rows = await prisma.$queryRaw<Array<{ total: bigint }>>(
|
||||
|
||||
@ -143,6 +143,26 @@ beforeEach(() => {
|
||||
})
|
||||
|
||||
describe('barcodePrintService', () => {
|
||||
it('limits external barcode imports to its own records and the bound supplier records', async () => {
|
||||
const externalUser = buildUser({
|
||||
id: 'external-user-id',
|
||||
businessRoles: ['EXTERNAL_INTAKE'],
|
||||
supplierName: '\u666f\u74f4\u5341\u65b9',
|
||||
})
|
||||
|
||||
await barcodePrintService.listRows(externalUser)
|
||||
|
||||
expect(mockFindMany).toHaveBeenNthCalledWith(1, {
|
||||
where: {
|
||||
OR: [
|
||||
{ createdBy: 'external-user-id' },
|
||||
{ normalizedSupplierName: '\u666f\u74f4\u5341\u65b9' },
|
||||
],
|
||||
},
|
||||
orderBy: { createdAt: 'asc' },
|
||||
})
|
||||
})
|
||||
|
||||
it('已绑定供应商名称时,条码导入记录只按精确名称查询', async () => {
|
||||
const result = await barcodePrintService.listRows(
|
||||
buildUser({
|
||||
|
||||
@ -41,6 +41,8 @@ jest.mock('../../repositories/intake-non-integrated-product.repository', () => (
|
||||
intakeNonIntegratedProductRepository: {
|
||||
listActive: jest.fn(),
|
||||
listActivePage: jest.fn(),
|
||||
listBarcodePrintSkuPage: jest.fn(),
|
||||
listBarcodePrintSkuCodes: jest.fn(),
|
||||
findActiveById: jest.fn(),
|
||||
findById: jest.fn(),
|
||||
findBySpu: jest.fn(),
|
||||
@ -442,7 +444,10 @@ describe('intakeNonIntegratedProductService', () => {
|
||||
})
|
||||
|
||||
it('uses the supplier name only for barcode-print system records when both bindings exist', async () => {
|
||||
mockedRepository.listActive.mockResolvedValue([] as never)
|
||||
mockedRepository.listBarcodePrintSkuPage.mockResolvedValue({
|
||||
records: [],
|
||||
total: 0,
|
||||
} as never)
|
||||
|
||||
await intakeNonIntegratedProductService.listForBarcodePrint({
|
||||
...baseUser,
|
||||
@ -451,8 +456,11 @@ describe('intakeNonIntegratedProductService', () => {
|
||||
supplierCode: 'N1280',
|
||||
})
|
||||
|
||||
expect(mockedRepository.listActive).toHaveBeenCalledWith({
|
||||
OR: [
|
||||
expect(mockedRepository.listBarcodePrintSkuPage).toHaveBeenCalledWith({
|
||||
skip: 0,
|
||||
take: 10_000,
|
||||
scopeWhere: {
|
||||
OR: [
|
||||
{
|
||||
skuItems: {
|
||||
some: {
|
||||
@ -469,7 +477,54 @@ describe('intakeNonIntegratedProductService', () => {
|
||||
array_contains: [{ supplierName: '浩远' }],
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('limits external barcode-print records to its own records and its bound supplier', async () => {
|
||||
mockedRepository.listBarcodePrintSkuPage.mockResolvedValue({
|
||||
records: [],
|
||||
total: 0,
|
||||
} as never)
|
||||
|
||||
await intakeNonIntegratedProductService.listForBarcodePrint({
|
||||
...baseUser,
|
||||
id: 'external-user-id',
|
||||
email: 'external@example.com',
|
||||
name: 'ExternalUser',
|
||||
businessRoles: ['EXTERNAL_INTAKE'] as const,
|
||||
supplierName: '\u666f\u74f4\u5341\u65b9',
|
||||
})
|
||||
|
||||
expect(mockedRepository.listBarcodePrintSkuPage).toHaveBeenCalledWith({
|
||||
skip: 0,
|
||||
take: 10_000,
|
||||
scopeWhere: {
|
||||
OR: [
|
||||
{ createdBy: 'external-user-id' },
|
||||
{
|
||||
OR: [
|
||||
{
|
||||
skuItems: {
|
||||
some: {
|
||||
manufacturer: {
|
||||
equals: '\u666f\u74f4\u5341\u65b9',
|
||||
mode: 'insensitive',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
payload: {
|
||||
path: ['factoryRows'],
|
||||
array_contains: [{ supplierName: '\u666f\u74f4\u5341\u65b9' }],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -177,6 +177,20 @@ function buildImportScope(user: AuthUser): BarcodePrintImportScope {
|
||||
return { normalizedSupplierName: '__unbound_supplier__' }
|
||||
}
|
||||
|
||||
if (intakeNonIntegratedScopeService.isExternalIntakeScopeUser(user)) {
|
||||
const supplierName = normalizeSupplierName(user.supplierName)
|
||||
const supplierCode = intakeNonIntegratedScopeService.getSupplierCode(user)
|
||||
|
||||
return {
|
||||
createdBy: user.id,
|
||||
...(supplierName
|
||||
? { normalizedSupplierName: supplierName }
|
||||
: supplierCode
|
||||
? { supplierCode }
|
||||
: {}),
|
||||
}
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
|
||||
@ -5742,7 +5742,57 @@ class IntakeNonIntegratedProductService {
|
||||
|
||||
private async buildBarcodePrintReadScopeWhere(user: AuthUser) {
|
||||
if (!intakeNonIntegratedScopeService.isSupplierScopeUser(user)) {
|
||||
return this.buildReadScopeWhere(user)
|
||||
const baseScope = await this.buildReadScopeWhere(user)
|
||||
if (!intakeNonIntegratedScopeService.isExternalIntakeScopeUser(user)) {
|
||||
return baseScope
|
||||
}
|
||||
|
||||
const supplierName = readText(user.supplierName)
|
||||
const supplierCode = intakeNonIntegratedScopeService.getSupplierCode(user)
|
||||
if (!supplierName && !supplierCode) return baseScope
|
||||
|
||||
const supplierScope: Prisma.IntakeNonIntegratedProductWhereInput = supplierName
|
||||
? {
|
||||
OR: [
|
||||
{
|
||||
skuItems: {
|
||||
some: {
|
||||
manufacturer: {
|
||||
equals: supplierName,
|
||||
mode: 'insensitive',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
payload: {
|
||||
path: ['factoryRows'],
|
||||
array_contains: [{ supplierName }],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {
|
||||
OR: [
|
||||
{
|
||||
skuItems: {
|
||||
some: {
|
||||
manufacturerCode: supplierCode,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
payload: {
|
||||
path: ['factoryRows'],
|
||||
array_contains: [{ supplierCode }],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
return {
|
||||
OR: [baseScope ?? {}, supplierScope],
|
||||
}
|
||||
}
|
||||
|
||||
const supplierName = readText(user.supplierName)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user