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>
136 lines
9.9 KiB
TypeScript
136 lines
9.9 KiB
TypeScript
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})`);
|
||
}
|