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