Files
geo/backend/dist/services/estimate.service.js

317 lines
13 KiB
JavaScript
Raw Normal View History

2026-02-04 00:11:19 +05:00
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EstimateService = void 0;
const client_1 = require("@prisma/client");
class EstimateService {
constructor(prisma) {
this.prisma = prisma;
}
async createEstimate(data) {
// Get direction
const direction = await this.prisma.surveyDirection.findUnique({
where: { code: data.directionCode },
});
if (!direction) {
throw new Error(`Direction not found: ${data.directionCode}`);
}
// Get default executor from settings
let executor = data.executor;
if (!executor) {
const defaultExecutor = await this.prisma.setting.findUnique({
where: { key: 'default_executor' },
});
executor = defaultExecutor?.value || 'Не указан';
}
// Generate estimate number (per owner)
const count = await this.prisma.estimate.count({
where: { ownerId: data.ownerId },
});
const number = `${count + 1}`;
const estimate = await this.prisma.estimate.create({
data: {
number,
directionId: direction.id,
ownerId: data.ownerId,
objectName: data.objectName,
customer: data.customer,
executor,
vatRate: data.vatRate ? new client_1.Prisma.Decimal(data.vatRate) : new client_1.Prisma.Decimal(20),
status: 'draft',
},
include: {
direction: true,
},
});
return estimate;
}
async updateEstimate(id, data) {
const updateData = {};
if (data.objectName)
updateData.objectName = data.objectName;
if (data.customer)
updateData.customer = data.customer;
if (data.executor)
updateData.executor = data.executor;
if (data.regionalCoef !== undefined)
updateData.regionalCoef = new client_1.Prisma.Decimal(data.regionalCoef);
if (data.inflationIndex !== undefined)
updateData.inflationIndex = new client_1.Prisma.Decimal(data.inflationIndex);
if (data.inflationDocRef)
updateData.inflationDocRef = data.inflationDocRef;
if (data.companyCoef !== undefined)
updateData.companyCoef = new client_1.Prisma.Decimal(data.companyCoef);
if (data.executorCoef !== undefined)
updateData.executorCoef = new client_1.Prisma.Decimal(data.executorCoef);
if (data.vatRate !== undefined)
updateData.vatRate = new client_1.Prisma.Decimal(data.vatRate);
if (data.status)
updateData.status = data.status;
const estimate = await this.prisma.estimate.update({
where: { id },
data: updateData,
include: {
direction: true,
items: { orderBy: { orderNumber: 'asc' } },
totals: { orderBy: { orderNumber: 'asc' } },
},
});
return estimate;
}
async addEstimateItem(estimateId, data) {
// Get max order number
const maxOrder = await this.prisma.estimateItem.aggregate({
where: { estimateId },
_max: { orderNumber: true },
});
const orderNumber = (maxOrder._max.orderNumber || 0) + 1;
// Calculate total price
const basePrice = new client_1.Prisma.Decimal(data.basePrice);
const quantity = new client_1.Prisma.Decimal(data.quantity);
let totalPrice = basePrice.mul(quantity);
if (data.coef1)
totalPrice = totalPrice.mul(new client_1.Prisma.Decimal(data.coef1));
if (data.coef2)
totalPrice = totalPrice.mul(new client_1.Prisma.Decimal(data.coef2));
if (data.coef3)
totalPrice = totalPrice.mul(new client_1.Prisma.Decimal(data.coef3));
const item = await this.prisma.estimateItem.create({
data: {
estimateId,
orderNumber,
sectionType: data.sectionType,
priceItemId: data.priceItemId || null,
workName: data.workName,
justification: data.justification || null,
basePrice,
quantity,
unit: data.unit || null,
coef1: data.coef1 ? new client_1.Prisma.Decimal(data.coef1) : null,
coef1Desc: data.coef1Desc || null,
coef2: data.coef2 ? new client_1.Prisma.Decimal(data.coef2) : null,
coef2Desc: data.coef2Desc || null,
coef3: data.coef3 ? new client_1.Prisma.Decimal(data.coef3) : null,
coef3Desc: data.coef3Desc || null,
totalPrice,
},
});
return item;
}
async updateEstimateItem(itemId, data) {
const updateData = {};
if (data.workName)
updateData.workName = data.workName;
if (data.justification)
updateData.justification = data.justification;
if (data.basePrice !== undefined)
updateData.basePrice = new client_1.Prisma.Decimal(data.basePrice);
if (data.quantity !== undefined)
updateData.quantity = new client_1.Prisma.Decimal(data.quantity);
if (data.unit)
updateData.unit = data.unit;
if (data.coef1 !== undefined)
updateData.coef1 = data.coef1 ? new client_1.Prisma.Decimal(data.coef1) : null;
if (data.coef1Desc !== undefined)
updateData.coef1Desc = data.coef1Desc;
if (data.coef2 !== undefined)
updateData.coef2 = data.coef2 ? new client_1.Prisma.Decimal(data.coef2) : null;
if (data.coef2Desc !== undefined)
updateData.coef2Desc = data.coef2Desc;
if (data.coef3 !== undefined)
updateData.coef3 = data.coef3 ? new client_1.Prisma.Decimal(data.coef3) : null;
if (data.coef3Desc !== undefined)
updateData.coef3Desc = data.coef3Desc;
// Recalculate total if price/quantity/coefs changed
const currentItem = await this.prisma.estimateItem.findUnique({ where: { id: itemId } });
if (currentItem) {
const basePrice = updateData.basePrice || currentItem.basePrice;
const quantity = updateData.quantity || currentItem.quantity;
let totalPrice = basePrice.mul(quantity);
const coef1 = updateData.coef1 !== undefined ? updateData.coef1 : currentItem.coef1;
const coef2 = updateData.coef2 !== undefined ? updateData.coef2 : currentItem.coef2;
const coef3 = updateData.coef3 !== undefined ? updateData.coef3 : currentItem.coef3;
if (coef1)
totalPrice = totalPrice.mul(coef1);
if (coef2)
totalPrice = totalPrice.mul(coef2);
if (coef3)
totalPrice = totalPrice.mul(coef3);
updateData.totalPrice = totalPrice;
}
const item = await this.prisma.estimateItem.update({
where: { id: itemId },
data: updateData,
});
return item;
}
async recalculateTotals(estimateId) {
const estimate = await this.prisma.estimate.findUnique({
where: { id: estimateId },
include: {
items: true,
},
});
if (!estimate) {
throw new Error('Estimate not found');
}
// Calculate section totals
const fieldWorks = estimate.items
.filter(i => i.sectionType === 'field')
.reduce((sum, i) => sum.add(i.totalPrice), new client_1.Prisma.Decimal(0));
const officeWorks = estimate.items
.filter(i => i.sectionType === 'office')
.reduce((sum, i) => sum.add(i.totalPrice), new client_1.Prisma.Decimal(0));
const laboratory = estimate.items
.filter(i => i.sectionType === 'laboratory')
.reduce((sum, i) => sum.add(i.totalPrice), new client_1.Prisma.Decimal(0));
const subtotal = estimate.items
.reduce((sum, i) => sum.add(i.totalPrice), new client_1.Prisma.Decimal(0));
// Apply coefficients
const regionalCoef = estimate.regionalCoef || new client_1.Prisma.Decimal(1);
const inflationIndex = estimate.inflationIndex || new client_1.Prisma.Decimal(1);
const companyCoef = estimate.companyCoef || new client_1.Prisma.Decimal(1);
const executorCoef = estimate.executorCoef || new client_1.Prisma.Decimal(1);
let totalWithoutVat = subtotal
.mul(regionalCoef)
.mul(inflationIndex)
.mul(companyCoef)
.mul(executorCoef);
const vatRate = estimate.vatRate || new client_1.Prisma.Decimal(20);
const vatAmount = totalWithoutVat.mul(vatRate).div(100);
const totalWithVat = totalWithoutVat.add(vatAmount);
// Update estimate
const updated = await this.prisma.estimate.update({
where: { id: estimateId },
data: {
totalFieldWorks: fieldWorks,
totalOfficeWorks: officeWorks,
totalLaboratory: laboratory,
subtotal,
totalWithoutVat,
vatAmount,
totalWithVat,
},
include: {
direction: true,
items: { orderBy: { orderNumber: 'asc' } },
totals: { orderBy: { orderNumber: 'asc' } },
},
});
// Update totals section
await this.updateTotalsSection(estimateId, estimate, updated);
return updated;
}
async updateTotalsSection(estimateId, estimate, updated) {
// Delete existing totals
await this.prisma.estimateTotal.deleteMany({ where: { estimateId } });
const totals = [];
let order = 1;
// Add totals rows
if (updated.regionalCoef && Number(updated.regionalCoef) !== 1) {
totals.push({
estimateId,
orderNumber: order++,
label: 'С районным коэффициентом',
description: `К=${updated.regionalCoef}`,
baseValue: updated.subtotal,
coefficient: updated.regionalCoef,
resultValue: updated.subtotal.mul(updated.regionalCoef),
});
}
if (updated.inflationIndex && Number(updated.inflationIndex) !== 1) {
const prevValue = totals.length > 0
? totals[totals.length - 1].resultValue
: updated.subtotal;
totals.push({
estimateId,
orderNumber: order++,
label: 'Перевод в текущие цены',
description: updated.inflationDocRef || `Индекс: ${updated.inflationIndex}`,
baseValue: prevValue,
coefficient: updated.inflationIndex,
resultValue: prevValue.mul(updated.inflationIndex),
});
}
if (updated.companyCoef && Number(updated.companyCoef) !== 1) {
const prevValue = totals.length > 0
? totals[totals.length - 1].resultValue
: updated.subtotal;
totals.push({
estimateId,
orderNumber: order++,
label: 'Коэффициент компании',
description: null,
baseValue: prevValue,
coefficient: updated.companyCoef,
resultValue: prevValue.mul(updated.companyCoef),
});
}
if (updated.executorCoef && Number(updated.executorCoef) !== 1) {
const prevValue = totals.length > 0
? totals[totals.length - 1].resultValue
: updated.subtotal;
totals.push({
estimateId,
orderNumber: order++,
label: `Коэффициент ${estimate.executor}`,
description: null,
baseValue: prevValue,
coefficient: updated.executorCoef,
resultValue: prevValue.mul(updated.executorCoef),
});
}
// Final totals
totals.push({
estimateId,
orderNumber: order++,
label: 'Итого без НДС:',
description: null,
baseValue: null,
coefficient: null,
resultValue: updated.totalWithoutVat,
});
totals.push({
estimateId,
orderNumber: order++,
label: `НДС ${updated.vatRate}%:`,
description: null,
baseValue: updated.totalWithoutVat,
coefficient: updated.vatRate.div(100),
resultValue: updated.vatAmount,
});
totals.push({
estimateId,
orderNumber: order++,
label: 'Всего с НДС:',
description: null,
baseValue: null,
coefficient: null,
resultValue: updated.totalWithVat,
});
// Create totals
for (const total of totals) {
await this.prisma.estimateTotal.create({ data: total });
}
}
}
exports.EstimateService = EstimateService;
//# sourceMappingURL=estimate.service.js.map