metallkart-erp/scripts/seed-referentials-helpers.ts
louis dcfdb8f142 fix(expense-codes): make all expense codes fully editable + move З11→Д, З12→Ж
Remove frontend-only restrictions: code/groupCode fields now editable in edit mode,
delete button enabled for all codes (soft-delete only). Move З11 (Аренда склада)
to group Д (Ремонт и обслуживание) and З12 (Логистика склад) to group Ж
(Производство субподряд) per financial director request.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-11 12:36:08 +04:00

136 lines
9.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { PrismaClient, PaymentCategory } from '@prisma/client';
type SeedRow = {
code: string;
labelRu: string;
parentCategory: PaymentCategory;
scope: 'INDIRECT_PRODUCTION' | 'INDIRECT_ADMIN' | 'DIRECT' | 'INCOME';
groupCodeOverride?: string;
};
const GROUPS: Record<string, string> = {
'А': 'Административные',
'Б': 'Маркетинг и реклама',
'В': 'Транспорт',
'Г': 'IT и связь',
'Д': 'Ремонт и обслуживание',
'Е': 'Материалы прямые',
'Ж': 'Производство (субподряд)',
'З': 'Склад и логистика',
'О': 'Прочее',
};
const ROWS: SeedRow[] = [
{ code: 'А11', labelRu: 'Зарплата административная', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А12', labelRu: 'Премии административные', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А13', labelRu: 'Соцстрахование', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А14', labelRu: 'Медстраховка', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А15', labelRu: 'Командировки', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А16', labelRu: 'Обучение сотрудников', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А17', labelRu: 'Подбор персонала', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А18', labelRu: 'Форма и спецодежда', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А19', labelRu: 'Питание персонала', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А20', labelRu: 'Транспорт персонала', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А21', labelRu: 'Канцтовары', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А22', labelRu: 'Связь и интернет', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А23', labelRu: 'Аренда офиса', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А24', labelRu: 'Коммунальные услуги (офис)', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А25', labelRu: 'Уборка офиса', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А26', labelRu: 'Охрана', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А27', labelRu: 'Бухгалтерия (аутсорс)', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А28', labelRu: 'Юридические услуги', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А29', labelRu: 'Аудит', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А30', labelRu: 'Консалтинг', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А31', labelRu: 'Банковское обслуживание', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А32', labelRu: 'Проценты по кредитам', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А33', labelRu: 'Страхование имущества', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А34', labelRu: 'Налоги и сборы', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'А35', labelRu: 'Штрафы и пени', parentCategory: 'EXCEPTIONAL', scope: 'INDIRECT_ADMIN' },
{ code: 'А36', labelRu: 'Представительские расходы', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'Б11', labelRu: 'Реклама', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'Б12', labelRu: 'Выставки и мероприятия', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'В11', labelRu: 'Доставка клиентам', parentCategory: 'DIRECT_PROJECT', scope: 'DIRECT' },
{ code: 'В12', labelRu: 'Транспорт внутренний', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'Г11', labelRu: 'IT-оборудование', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'Г12', labelRu: 'Лицензии ПО', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'Д11', labelRu: 'Ремонт офиса', parentCategory: 'ADMINISTRATIVE', scope: 'INDIRECT_ADMIN' },
{ code: 'Д12', labelRu: 'Ремонт оборудования', parentCategory: 'PRODUCTION_SUPPLY', scope: 'INDIRECT_PRODUCTION' },
{ code: 'Д13', labelRu: 'Обслуживание оборудования', parentCategory: 'PRODUCTION_SUPPLY', scope: 'INDIRECT_PRODUCTION' },
{ code: 'Е11', labelRu: 'Металл листовой', parentCategory: 'DIRECT_PROJECT', scope: 'DIRECT' },
{ code: 'Е12', labelRu: 'Металл профильный', parentCategory: 'DIRECT_PROJECT', scope: 'DIRECT' },
{ code: 'Е13', labelRu: 'Крепёж и метизы', parentCategory: 'DIRECT_PROJECT', scope: 'DIRECT' },
{ code: 'Е14', labelRu: 'Лакокраска и расходники ЛКМ', parentCategory: 'DIRECT_PROJECT', scope: 'DIRECT' },
{ code: 'Ж11', labelRu: 'Лазерная резка (субподряд)', parentCategory: 'DIRECT_PROJECT', scope: 'DIRECT' },
{ code: 'Ж12', labelRu: 'Гибка (субподряд)', parentCategory: 'DIRECT_PROJECT', scope: 'DIRECT' },
{ code: 'Ж13', labelRu: 'Сварка (субподряд)', parentCategory: 'DIRECT_PROJECT', scope: 'DIRECT' },
{ code: 'Ж14', labelRu: 'Порошковая покраска', parentCategory: 'DIRECT_PROJECT', scope: 'DIRECT' },
{ code: 'Ж15', labelRu: 'Гальваника', parentCategory: 'DIRECT_PROJECT', scope: 'DIRECT' },
{ code: 'Ж16', labelRu: 'Механическая обработка', parentCategory: 'DIRECT_PROJECT', scope: 'DIRECT' },
{ code: 'Ж17', labelRu: 'Сборка (субподряд)', parentCategory: 'DIRECT_PROJECT', scope: 'DIRECT' },
{ code: 'Ж18', labelRu: 'Логистика производства', parentCategory: 'PRODUCTION_SUPPLY', scope: 'INDIRECT_PRODUCTION' },
{ code: 'Ж19', labelRu: 'Расходники производства', parentCategory: 'PRODUCTION_SUPPLY', scope: 'INDIRECT_PRODUCTION' },
{ code: 'Ж20', labelRu: 'Инструменты производства', parentCategory: 'PRODUCTION_SUPPLY', scope: 'INDIRECT_PRODUCTION' },
{ code: 'З11', labelRu: 'Аренда склада', parentCategory: 'PRODUCTION_SUPPLY', scope: 'INDIRECT_PRODUCTION', groupCodeOverride: 'Д' },
{ code: 'З12', labelRu: 'Логистика склад', parentCategory: 'PRODUCTION_SUPPLY', scope: 'INDIRECT_PRODUCTION', groupCodeOverride: 'Ж' },
{ code: 'О11', labelRu: 'Непредвиденные расходы', parentCategory: 'EXCEPTIONAL', scope: 'INDIRECT_ADMIN' },
{ code: 'О12', labelRu: 'Резерв', parentCategory: 'EXCEPTIONAL', scope: 'INDIRECT_ADMIN' },
{ code: 'О13', labelRu: 'Прочее', parentCategory: 'EXCEPTIONAL', scope: 'INDIRECT_ADMIN' },
];
const INDIRECT_SUPPLIER_TAGS: Array<{ name: string; label: string }> = [
{ name: 'OFFICE_SUPPLIES', label: 'Канцтовары' },
{ name: 'COMMUNICATIONS_IND', label: 'Связь и интернет' },
{ name: 'UTILITIES', label: 'Коммунальные услуги' },
{ name: 'OFFICE_MAINTENANCE', label: 'Обслуживание офиса' },
{ name: 'LOGISTICS_ADMIN', label: 'Логистика админ' },
{ name: 'CONSULTING_SVC', label: 'Консалтинг/аудит' },
{ name: 'OTHER_INDIRECT', label: 'Прочие косвенные' },
];
export async function seedExpenseCodes(prisma: PrismaClient) {
let ecCreated = 0;
let ecUpdated = 0;
for (let i = 0; i < ROWS.length; i++) {
const row = ROWS[i];
const groupCode = row.groupCodeOverride ?? row.code[0];
const groupLabel = GROUPS[groupCode] ?? 'Autre';
const data = {
code: row.code,
labelRu: row.labelRu,
groupCode,
groupLabel,
parentCategory: row.parentCategory,
scope: row.scope,
isSystem: true,
isActive: true,
sortOrder: i,
};
const existing = await prisma.expenseCode.findUnique({ where: { code: row.code } });
if (existing) {
await prisma.expenseCode.update({ where: { code: row.code }, data });
ecUpdated++;
} else {
await prisma.expenseCode.create({ data });
ecCreated++;
}
}
console.log(` expense_codes: ${ecCreated} created, ${ecUpdated} updated (total ${ROWS.length})`);
let tagCreated = 0;
let tagUpdated = 0;
for (const t of INDIRECT_SUPPLIER_TAGS) {
const existing = await prisma.supplierTag.findUnique({ where: { name: t.name } });
if (existing) {
await prisma.supplierTag.update({ where: { name: t.name }, data: { label: t.label, isIndirect: true } });
tagUpdated++;
} else {
await prisma.supplierTag.create({ data: { name: t.name, label: t.label, isIndirect: true } });
tagCreated++;
}
}
console.log(` supplier_tags: ${tagCreated} created, ${tagUpdated} updated (total ${INDIRECT_SUPPLIER_TAGS.length})`);
}