@@ -5,6 +5,30 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
const PREFIX = 'DK';
|
||||
const PAD = 6;
|
||||
const RE = /^DK(\d+)$/;
|
||||
|
||||
async function nextDkCode() {
|
||||
const [items, skus] = await Promise.all([
|
||||
prisma.commonProductItem.findMany({
|
||||
where: { skuCode: { startsWith: PREFIX } },
|
||||
select: { skuCode: true },
|
||||
}),
|
||||
prisma.commonProductSku.findMany({
|
||||
where: { skuCode: { startsWith: PREFIX } },
|
||||
select: { skuCode: true },
|
||||
}),
|
||||
]);
|
||||
let maxSeq = 0;
|
||||
for (const row of [...items, ...skus]) {
|
||||
const m = RE.exec(row.skuCode);
|
||||
if (!m) continue;
|
||||
const n = Number(m[1]);
|
||||
if (Number.isFinite(n) && n > maxSeq) maxSeq = n;
|
||||
}
|
||||
return `${PREFIX}${String(maxSeq + 1).padStart(PAD, '0')}`;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const products = await prisma.commonProductItem.findMany({
|
||||
@@ -13,10 +37,11 @@ async function main() {
|
||||
let created = 0;
|
||||
for (const p of products) {
|
||||
if (p.skus.length > 0) continue;
|
||||
const skuCode = RE.test(p.skuCode) ? p.skuCode : await nextDkCode();
|
||||
await prisma.commonProductSku.create({
|
||||
data: {
|
||||
productId: p.id,
|
||||
skuCode: p.skuCode,
|
||||
skuCode,
|
||||
barcode69: p.barcode69,
|
||||
specKey: '',
|
||||
specText: p.spec,
|
||||
@@ -32,8 +57,14 @@ async function main() {
|
||||
sortOrder: 0,
|
||||
},
|
||||
});
|
||||
if (p.skuCode !== skuCode) {
|
||||
await prisma.commonProductItem.update({
|
||||
where: { id: p.id },
|
||||
data: { skuCode },
|
||||
});
|
||||
}
|
||||
created += 1;
|
||||
console.log(`created default sku for product ${p.id} ${p.skuCode}`);
|
||||
console.log(`created default sku for product ${p.id} ${skuCode}`);
|
||||
}
|
||||
console.log(`done, created=${created}, scanned=${products.length}`);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
-- 发票开票品类:酒水类 / 餐饮类(票种仍为增值税普通发票)
|
||||
-- 存量申请默认酒水类,与 Prisma InvoiceCategory 默认值一致
|
||||
|
||||
ALTER TABLE `user_invoice`
|
||||
ADD COLUMN `invoice_category` VARCHAR(16) NOT NULL DEFAULT 'LIQUOR' AFTER `invoice_kind`;
|
||||
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* 将存量商品 / SKU 的 sku_code 全部改成 DK + 6 位数字。
|
||||
* 已是 DK 数字码的保持不变;其余先写临时码再分配,避免唯一约束冲突。
|
||||
*
|
||||
* 用法:cd server/dukang-api && npx ts-node prisma/rewrite-sku-codes-dk.ts
|
||||
*/
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
const PREFIX = 'DK';
|
||||
const PAD = 6;
|
||||
const RE = /^DK(\d+)$/;
|
||||
|
||||
function isDk(code: string) {
|
||||
return RE.test(code);
|
||||
}
|
||||
|
||||
function formatCode(seq: number) {
|
||||
return `${PREFIX}${String(seq).padStart(PAD, '0')}`;
|
||||
}
|
||||
|
||||
async function maxExistingSeq(): Promise<number> {
|
||||
const [items, skus] = await Promise.all([
|
||||
prisma.commonProductItem.findMany({
|
||||
where: { skuCode: { startsWith: PREFIX } },
|
||||
select: { skuCode: true },
|
||||
}),
|
||||
prisma.commonProductSku.findMany({
|
||||
where: { skuCode: { startsWith: PREFIX } },
|
||||
select: { skuCode: true },
|
||||
}),
|
||||
]);
|
||||
let maxSeq = 0;
|
||||
for (const row of [...items, ...skus]) {
|
||||
const m = RE.exec(row.skuCode);
|
||||
if (!m) continue;
|
||||
const n = Number(m[1]);
|
||||
if (Number.isFinite(n) && n > maxSeq) maxSeq = n;
|
||||
}
|
||||
return maxSeq;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const skus = await prisma.commonProductSku.findMany({
|
||||
orderBy: [{ productId: 'asc' }, { id: 'asc' }],
|
||||
select: { id: true, productId: true, skuCode: true, isDefault: true },
|
||||
});
|
||||
const products = await prisma.commonProductItem.findMany({
|
||||
select: { id: true, skuCode: true },
|
||||
});
|
||||
|
||||
const skuNeed = skus.filter((s) => !isDk(s.skuCode));
|
||||
const productNeed = products.filter((p) => !isDk(p.skuCode));
|
||||
console.log(`sku total=${skus.length} rewrite=${skuNeed.length}`);
|
||||
console.log(`product total=${products.length} rewrite=${productNeed.length}`);
|
||||
|
||||
await prisma.$transaction(async (tx) => {
|
||||
for (const s of skuNeed) {
|
||||
await tx.commonProductSku.update({
|
||||
where: { id: s.id },
|
||||
data: { skuCode: `TMP${s.id.toString()}` },
|
||||
});
|
||||
}
|
||||
for (const p of productNeed) {
|
||||
await tx.commonProductItem.update({
|
||||
where: { id: p.id },
|
||||
data: { skuCode: `TMPP${p.id.toString()}` },
|
||||
});
|
||||
}
|
||||
|
||||
const seqStart = await (async () => {
|
||||
const [items, skuRows] = await Promise.all([
|
||||
tx.commonProductItem.findMany({
|
||||
where: { skuCode: { startsWith: PREFIX } },
|
||||
select: { skuCode: true },
|
||||
}),
|
||||
tx.commonProductSku.findMany({
|
||||
where: { skuCode: { startsWith: PREFIX } },
|
||||
select: { skuCode: true },
|
||||
}),
|
||||
]);
|
||||
let maxSeq = 0;
|
||||
for (const row of [...items, ...skuRows]) {
|
||||
const m = RE.exec(row.skuCode);
|
||||
if (!m) continue;
|
||||
const n = Number(m[1]);
|
||||
if (Number.isFinite(n) && n > maxSeq) maxSeq = n;
|
||||
}
|
||||
return maxSeq;
|
||||
})();
|
||||
|
||||
let seq = seqStart;
|
||||
const defaultSkuCodeByProduct = new Map<string, string>();
|
||||
|
||||
for (const s of skuNeed) {
|
||||
seq += 1;
|
||||
const code = formatCode(seq);
|
||||
await tx.commonProductSku.update({
|
||||
where: { id: s.id },
|
||||
data: { skuCode: code },
|
||||
});
|
||||
if (s.isDefault) defaultSkuCodeByProduct.set(s.productId.toString(), code);
|
||||
console.log(`sku ${s.id} ${s.skuCode} -> ${code}`);
|
||||
}
|
||||
|
||||
for (const p of productNeed) {
|
||||
const fromDefault = defaultSkuCodeByProduct.get(p.id.toString());
|
||||
let code = fromDefault;
|
||||
if (!code) {
|
||||
seq += 1;
|
||||
code = formatCode(seq);
|
||||
}
|
||||
await tx.commonProductItem.update({
|
||||
where: { id: p.id },
|
||||
data: { skuCode: code },
|
||||
});
|
||||
console.log(`product ${p.id} ${p.skuCode} -> ${code}`);
|
||||
}
|
||||
|
||||
const stillDefault = await tx.commonProductSku.findMany({
|
||||
where: { isDefault: true },
|
||||
select: { productId: true, skuCode: true },
|
||||
});
|
||||
for (const s of stillDefault) {
|
||||
if (!isDk(s.skuCode)) continue;
|
||||
await tx.commonProductItem.updateMany({
|
||||
where: { id: s.productId, NOT: { skuCode: s.skuCode } },
|
||||
data: { skuCode: s.skuCode },
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const leftoverSku = await prisma.commonProductSku.count({
|
||||
where: { NOT: { skuCode: { startsWith: PREFIX } } },
|
||||
});
|
||||
const leftoverProduct = await prisma.commonProductItem.count({
|
||||
where: { NOT: { skuCode: { startsWith: PREFIX } } },
|
||||
});
|
||||
console.log(`done leftoverSku=${leftoverSku} leftoverProduct=${leftoverProduct} maxWas=${await maxExistingSeq()}`);
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(() => prisma.$disconnect());
|
||||
@@ -146,6 +146,12 @@ enum InvoiceKind {
|
||||
SPECIAL
|
||||
}
|
||||
|
||||
/// C 端开票品类(增值税票种固定普通发票)
|
||||
enum InvoiceCategory {
|
||||
LIQUOR
|
||||
CATERING
|
||||
}
|
||||
|
||||
enum InvoiceStatus {
|
||||
PENDING
|
||||
ISSUED
|
||||
@@ -1678,6 +1684,8 @@ model UserInvoice {
|
||||
userId BigInt @map("user_id") @db.UnsignedBigInt
|
||||
titleType InvoiceTitleType @map("title_type")
|
||||
invoiceKind InvoiceKind @map("invoice_kind")
|
||||
/// 酒水类 / 餐饮类;历史单默认酒水类
|
||||
invoiceCategory InvoiceCategory @default(LIQUOR) @map("invoice_category")
|
||||
titleName String @map("title_name") @db.VarChar(128)
|
||||
taxNo String? @map("tax_no") @db.VarChar(32)
|
||||
addressPhone String? @map("address_phone") @db.VarChar(256)
|
||||
|
||||
Reference in New Issue
Block a user