fix: scope external barcode print records

This commit is contained in:
WuYongJie 2026-08-03 16:28:40 +08:00
parent 076075a3a8
commit c58f642574
5 changed files with 173 additions and 17 deletions

View File

@ -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 }>>(

View File

@ -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({

View File

@ -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' }],
},
},
],
},
],
},
})
})

View File

@ -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 {}
}

View File

@ -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)