Files
geo/backend/dist/services/estimate.service.js
2026-02-04 00:11:19 +05:00

317 lines
13 KiB
JavaScript
Executable File
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.
"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