/
diloytd
/
shield
Обзор
Документация
Войти
/
diloytd
/
shield
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/services/instanceService.js
217 строк
7 KB
diloytd
Перепроектировать shields.json и поддержать ТУ, сертификаты и новые условия.
22 июл 2026, 17:14
22 июл 2026, 17:14
fb8b887
Код
Авторство
О чём код?
/** * Сервис получения экземпляров изделий из SQLite. * Используется для отображения списка экземпляров и выбора одного для присвоения шильда. */ const { getDatabase } = require('../database/database'); const { resolveMarking } = require('./journalService'); const { formatCertificatesForDisplay, parseCertificates } = require('../utils/certificateUtils'); /** * SQL-запрос для получения всех экземпляров изделий с данными заказа и первым присвоенным шильдом. * @type {string} */ const INSTANCES_SELECT_SQL = ` SELECT pi.id AS instanceId, o.number AS orderNumber, op.number AS positionNumber, pi.number AS instanceNumber, lp.type AS productType, lp.systemName, ( SELECT COUNT(*) FROM Shields s WHERE s.instanceId = pi.id ) AS shieldsCount, sh.shieldType, sh.shieldNumber, sh.marking, sh.fireResistance AS shieldFireResistance, sh.TU, sh.certificates, sh.productionDate FROM ProductInstances pi INNER JOIN LogicalProducts lp ON pi.logicalProductId = lp.id INNER JOIN OrderPositions op ON lp.positionId = op.id INNER JOIN Orders o ON op.orderId = o.id LEFT JOIN ( SELECT instanceId, shieldType, shieldNumber, marking, fireResistance, TU, certificates, productionDate, ROW_NUMBER() OVER (PARTITION BY instanceId ORDER BY id) AS shieldIndex FROM Shields ) AS sh ON sh.instanceId = pi.id AND sh.shieldIndex = 1 ORDER BY o.number, op.number, lp.id, pi.number `; /** * Преобразует строку из SQLite в объект экземпляра для UI. * @param {Object} dbRow - Строка результата SQL-запроса. * @returns {Object} Экземпляр изделия для передачи в UI. */ const mapDbRowToInstanceRow = (dbRow) => { const shieldsCount = Number(dbRow.shieldsCount) || 0; const hasShield = shieldsCount > 0; const shieldType = hasShield ? (dbRow.shieldType ?? '') : ''; const shieldFireResistance = hasShield ? (dbRow.shieldFireResistance ?? '') : ''; return { instanceId: dbRow.instanceId, orderNumber: dbRow.orderNumber ?? '', positionNumber: dbRow.positionNumber, instanceNumber: dbRow.instanceNumber, productType: dbRow.productType ?? '', systemName: dbRow.systemName ?? '', hasShield, shieldsCount, marking: hasShield ? resolveMarking(dbRow.marking, shieldType, shieldFireResistance) : '', shieldNumber: hasShield ? (dbRow.shieldNumber ?? '') : '', shieldType, shieldFireResistance, tu: hasShield ? (dbRow.TU ?? '') : '', certificates: hasShield ? formatCertificatesForDisplay(parseCertificates(dbRow.certificates)) : '', productionDate: hasShield ? (dbRow.productionDate ?? '') : '', }; }; /** * Возвращает идентификатор заказа для экземпляра изделия. * @param {import('better-sqlite3').Database} db - Подключение к SQLite. * @param {number} instanceId - Идентификатор экземпляра изделия. * @returns {number|null} Идентификатор заказа или null, если экземпляр не найден. */ const getOrderIdForInstance = (db, instanceId) => { const orderRow = db.prepare(` SELECT op.orderId AS orderId FROM ProductInstances pi INNER JOIN LogicalProducts lp ON pi.logicalProductId = lp.id INNER JOIN OrderPositions op ON lp.positionId = op.id WHERE pi.id = ? `).get(instanceId); if (!orderRow?.orderId) { return null; } return Number(orderRow.orderId); }; /** * Возвращает количество экземпляров изделий, оставшихся у заказа. * @param {import('better-sqlite3').Database} db - Подключение к SQLite. * @param {number} orderId - Идентификатор заказа. * @returns {number} Количество экземпляров изделий заказа. */ const getOrderInstanceCount = (db, orderId) => { const countRow = db.prepare(` SELECT COUNT(*) AS count FROM ProductInstances pi INNER JOIN LogicalProducts lp ON pi.logicalProductId = lp.id INNER JOIN OrderPositions op ON lp.positionId = op.id WHERE op.orderId = ? `).get(orderId); return Number(countRow?.count) || 0; }; /** * Полностью удаляет заказ и связанные данные каскадно, включая запись журнала. * @param {import('better-sqlite3').Database} db - Подключение к SQLite. * @param {number} orderId - Идентификатор заказа. * @returns {void} */ const deleteOrderCompletely = (db, orderId) => { db.prepare('DELETE FROM Orders WHERE id = ?').run(orderId); }; /** * Удаляет заказ, если у него не осталось экземпляров изделий. * @param {import('better-sqlite3').Database} db - Подключение к SQLite. * @param {number} orderId - Идентификатор заказа. * @returns {boolean} true, если заказ был удалён. */ const deleteOrderIfEmpty = (db, orderId) => { if (getOrderInstanceCount(db, orderId) > 0) { return false; } deleteOrderCompletely(db, orderId); return true; }; /** * Получает все экземпляры изделий из SQLite. * @returns {{ rows: Array<Object>, rowCount: number }} Список экземпляров и их количество. */ const getAllInstances = () => { const db = getDatabase(); const selectInstances = db.prepare(INSTANCES_SELECT_SQL); const dbRows = selectInstances.all(); const rows = dbRows.map(mapDbRowToInstanceRow); return { rows, rowCount: rows.length, }; }; /** * Удаляет экземпляр изделия из SQLite по идентификатору. * Связанные шильды удаляются каскадно через ON DELETE CASCADE. * @param {number} instanceId - Идентификатор экземпляра изделия. * @returns {{ deleted: boolean }} Результат удаления. * @throws {Error} Если идентификатор некорректен или экземпляр не найден. */ const deleteInstance = (instanceId) => { const normalizedInstanceId = Number(instanceId); if (!Number.isInteger(normalizedInstanceId) || normalizedInstanceId <= 0) { throw new Error('Некорректный идентификатор экземпляра.'); } const db = getDatabase(); const orderId = getOrderIdForInstance(db, normalizedInstanceId); if (!orderId) { throw new Error('Экземпляр изделия не найден.'); } const deleteInstanceTransaction = db.transaction(() => { const deleteInstanceStmt = db.prepare('DELETE FROM ProductInstances WHERE id = ?'); const result = deleteInstanceStmt.run(normalizedInstanceId); if (result.changes === 0) { throw new Error('Экземпляр изделия не найден.'); } deleteOrderIfEmpty(db, orderId); }); deleteInstanceTransaction(); return { deleted: true, }; }; module.exports = { INSTANCES_SELECT_SQL, deleteInstance, deleteOrderCompletely, deleteOrderIfEmpty, getAllInstances, getOrderIdForInstance, getOrderInstanceCount, };