@@ -747,6 +884,8 @@ const DB = {
set warehouse(v) { this._save('warehouse', v); },
get issuances() { return this._load('issuances'); },
set issuances(v) { this._save('issuances', v); },
+ get norms() { return this._load('norms'); },
+ set norms(v) { this._save('norms', v); },
};
function uid() { return Date.now().toString(36) + Math.random().toString(36).slice(2, 7); }
@@ -769,11 +908,12 @@ function daysBetween(d1, d2) {
function seedDemo() {
if (DB.employees.length > 0) return;
DB.employees = [
- { id: uid(), tabNum: '00001', fullName: 'Иванов Иван Иванович', position: 'Инженер', department: 'Технический отдел', dateHired: '2020-03-15' },
- { id: uid(), tabNum: '00002', fullName: 'Петрова Анна Сергеевна', position: 'Монтажник', department: 'Линейный цех', dateHired: '2021-06-01' },
- { id: uid(), tabNum: '00003', fullName: 'Сериков Асхат Нурланович', position: 'Электрик', department: 'Энергоцех', dateHired: '2019-11-10' },
- { id: uid(), tabNum: '00004', fullName: 'Ким Елена Викторовна', position: 'Техник', department: 'Технический отдел', dateHired: '2022-01-20' },
- { id: uid(), tabNum: '00005', fullName: 'Нургалиев Даурен Кайратович', position: 'Сварщик', department: 'Ремонтный цех', dateHired: '2018-08-05' },
+ { id: uid(), tabNum: '00001', fullName: 'Иванов Иван Иванович', position: 'Кабельщик-спайщик', department: 'Линейный цех', climateZone: '1', dateHired: '2020-03-15' },
+ { id: uid(), tabNum: '00002', fullName: 'Петрова Анна Сергеевна', position: 'Электромонтер', department: 'Энергоцех', climateZone: '2', dateHired: '2021-06-01' },
+ { id: uid(), tabNum: '00003', fullName: 'Сериков Асхат Нурланович', position: 'Электромонтер', department: 'Энергоцех', climateZone: '3', dateHired: '2019-11-10' },
+ { id: uid(), tabNum: '00004', fullName: 'Ким Елена Викторовна', position: 'Инженер', department: 'Технический отдел', climateZone: '0', dateHired: '2022-01-20' },
+ { id: uid(), tabNum: '00005', fullName: 'Нургалиев Даурен Кайратович', position: 'Сварщик', department: 'Ремонтный цех', climateZone: '2', dateHired: '2018-08-05' },
+ { id: uid(), tabNum: '00006', fullName: 'Ахметов Тимур Болатович', position: 'Кабельщик-спайщик', department: 'Линейный цех', climateZone: '2', dateHired: '2023-02-01' },
];
DB.siz = [
{ id: uid(), name: 'Каска защитная', type: 'Головы', protection: '1 класс', standard: 'ГОСТ 12.4.128-83', wearMonths: 24, unit: 'шт.' },
@@ -783,6 +923,33 @@ function seedDemo() {
{ id: uid(), name: 'Ботинки кожаные', type: 'Ног', protection: 'Мун 200', standard: 'ГОСТ 12.4.137-2001', wearMonths: 12, unit: 'пар' },
{ id: uid(), name: 'Костюм х/б', type: 'Спецодежда', protection: 'Ми', standard: 'ГОСТ 12.4.280-2014', wearMonths: 12, unit: 'компл.' },
{ id: uid(), name: 'Пояс предохранительный', type: 'Прочее', protection: '1 класс', standard: 'ГОСТ 12.4.089-86', wearMonths: 36, unit: 'шт.' },
+ { id: uid(), name: 'Костюм летний х/б', type: 'Спецодежда', protection: 'Ми', standard: 'ГОСТ 12.4.280-2014', wearMonths: 12, unit: 'компл.' },
+ { id: uid(), name: 'Костюм зимний утепленный', type: 'Спецодежда', protection: 'Тн', standard: 'ГОСТ 12.4.236-2011', wearMonths: 36, unit: 'компл.' },
+ { id: uid(), name: 'Рукавицы брезентовые', type: 'Рук', protection: 'Ми', standard: 'ГОСТ 12.4.010-75', wearMonths: 1, unit: 'пар' },
+ { id: uid(), name: 'Диэлектрические перчатки', type: 'Рук', protection: 'Эн', standard: 'ГОСТ 12.4.307-2016', wearMonths: 12, unit: 'пар' },
+ { id: uid(), name: 'Диэлектрические боты', type: 'Ног', protection: 'Эн', standard: 'ГОСТ 12.4.307-2016', wearMonths: 36, unit: 'пар' },
+ { id: uid(), name: 'Щиток сварочный', type: 'Глаз', protection: '3 класс', standard: 'ГОСТ 12.4.254-2013', wearMonths: 24, unit: 'шт.' },
+ ];
+ DB.norms = [
+ { id: uid(), position: 'Кабельщик-спайщик', climateZone: '0', sizId: DB.siz[0].id, wearMonths: 24, quantity: 1, note: '' },
+ { id: uid(), position: 'Кабельщик-спайщик', climateZone: '0', sizId: DB.siz[2].id, wearMonths: 1, quantity: 12, note: '' },
+ { id: uid(), position: 'Кабельщик-спайщик', climateZone: '0', sizId: DB.siz[4].id, wearMonths: 12, quantity: 1, note: '' },
+ { id: uid(), position: 'Кабельщик-спайщик', climateZone: '0', sizId: DB.siz[7].id, wearMonths: 12, quantity: 1, note: 'Летний' },
+ { id: uid(), position: 'Кабельщик-спайщик', climateZone: '0', sizId: DB.siz[8].id, wearMonths: 36, quantity: 1, note: 'Зимний' },
+ { id: uid(), position: 'Электромонтер', climateZone: '0', sizId: DB.siz[0].id, wearMonths: 24, quantity: 1, note: '' },
+ { id: uid(), position: 'Электромонтер', climateZone: '0', sizId: DB.siz[1].id, wearMonths: 12, quantity: 1, note: '' },
+ { id: uid(), position: 'Электромонтер', climateZone: '0', sizId: DB.siz[4].id, wearMonths: 12, quantity: 1, note: '' },
+ { id: uid(), position: 'Электромонтер', climateZone: '0', sizId: DB.siz[10].id, wearMonths: 12, quantity: 1, note: '' },
+ { id: uid(), position: 'Электромонтер', climateZone: '0', sizId: DB.siz[11].id, wearMonths: 36, quantity: 1, note: '' },
+ { id: uid(), position: 'Электромонтер', climateZone: '1', sizId: DB.siz[8].id, wearMonths: 36, quantity: 1, note: 'Зимний, пояс 1: 3 года' },
+ { id: uid(), position: 'Электромонтер', climateZone: '2', sizId: DB.siz[8].id, wearMonths: 30, quantity: 1, note: 'Зимний, пояс 2: 2,5 года' },
+ { id: uid(), position: 'Электромонтер', climateZone: '3', sizId: DB.siz[8].id, wearMonths: 24, quantity: 1, note: 'Зимний, пояс 3: 2 года' },
+ { id: uid(), position: 'Сварщик', climateZone: '0', sizId: DB.siz[0].id, wearMonths: 24, quantity: 1, note: '' },
+ { id: uid(), position: 'Сварщик', climateZone: '0', sizId: DB.siz[12].id, wearMonths: 24, quantity: 1, note: '' },
+ { id: uid(), position: 'Сварщик', climateZone: '0', sizId: DB.siz[9].id, wearMonths: 1, quantity: 12, note: '' },
+ { id: uid(), position: 'Сварщик', climateZone: '0', sizId: DB.siz[4].id, wearMonths: 12, quantity: 1, note: '' },
+ { id: uid(), position: 'Инженер', climateZone: '0', sizId: DB.siz[0].id, wearMonths: 24, quantity: 1, note: '' },
+ { id: uid(), position: 'Инженер', climateZone: '0', sizId: DB.siz[1].id, wearMonths: 12, quantity: 1, note: '' },
];
DB.warehouse = [
{ id: uid(), sizId: DB.siz[0].id, quantity: 50, date: '2025-06-01', batch: 'Б-2025001', supplier: 'ТОО Спецзащита', opType: 'in' },
@@ -812,6 +979,7 @@ document.querySelectorAll('.nav button').forEach(btn => {
document.getElementById(btn.dataset.panel).classList.add('active');
if (btn.dataset.panel === 'tab-reports') renderReports();
if (btn.dataset.panel === 'tab-control') renderControl();
+ if (btn.dataset.panel === 'tab-norms') renderNorms();
});
});
@@ -832,6 +1000,7 @@ function openEmployeeModal(emp) {
document.getElementById('empPosition').value = emp ? emp.position : '';
document.getElementById('empDepartment').value = emp ? emp.department : '';
document.getElementById('empDateHired').value = emp ? emp.dateHired : '';
+ document.getElementById('empClimateZone').value = emp ? (emp.climateZone || '0') : '0';
openModal('employeeModal');
}
@@ -845,6 +1014,7 @@ function saveEmployee() {
fullName: name,
position: document.getElementById('empPosition').value.trim(),
department: document.getElementById('empDepartment').value.trim(),
+ climateZone: document.getElementById('empClimateZone').value || '0',
dateHired: document.getElementById('empDateHired').value,
};
let list = DB.employees;
@@ -1032,7 +1202,170 @@ function renderWarehouse() {
document.getElementById('whEmpty').style.display = list.length ? 'none' : 'block';
}
-// ===================== ISSUANCES =====================
+// ===================== NORMS =====================
+function fillNormSizSelect() {
+ const sel = document.getElementById('normSizId');
+ sel.innerHTML = '
' +
+ DB.siz.map(s => `
`).join('');
+}
+
+function fillPositionsDatalist() {
+ const list = document.getElementById('positionsList');
+ const positions = [...new Set([...DB.norms.map(n => n.position), ...DB.employees.map(e => e.position)].filter(Boolean))];
+ list.innerHTML = positions.map(p => `
' +
+ DB.employees.map(e => `
`).join('');
+ if (v) sel.value = v;
+};
+document.getElementById('issEmployeeId').addEventListener('change', showNormsForEmployee);
function fillEmployeeSelect() {
const sel = document.getElementById('issEmployeeId');
sel.innerHTML = '
' +
@@ -1045,11 +1378,13 @@ function openIssuanceModal(iss) {
document.getElementById('issQuantity').value = iss ? iss.quantity : '1';
document.getElementById('issDate').value = iss ? iss.dateIssued : todayStr();
document.getElementById('issNotes').value = iss ? iss.notes : '';
+ document.getElementById('issNormHint').style.display = 'none';
fillEmployeeSelect();
fillSizSelect('issSizId', '— Выберите СИЗ —');
if (iss) {
document.getElementById('issEmployeeId').value = iss.employeeId;
document.getElementById('issSizId').value = iss.sizId;
+ showNormsForEmployee();
}
openModal('issuanceModal');
}
@@ -1281,6 +1616,17 @@ function exportAllToExcel() {
const ws4 = XLSX.utils.json_to_sheet(iss);
XLSX.utils.book_append_sheet(wb, ws4, 'Выдача СИЗ');
+ const norms = DB.norms.map(n => ({
+ 'Должность': n.position,
+ 'Климатический пояс': climateZoneLabel(n.climateZone),
+ 'СИЗ': getSizName(n.sizId),
+ 'Срок носки': monthsToText(n.wearMonths),
+ 'Количество': n.quantity,
+ 'Примечание': n.note,
+ }));
+ const ws5 = XLSX.utils.json_to_sheet(norms);
+ XLSX.utils.book_append_sheet(wb, ws5, 'Нормы выдачи');
+
XLSX.writeFile(wb, 'Учет_СИЗ_' + new Date().toISOString().slice(0, 10) + '.xlsx');
}
@@ -1297,6 +1643,7 @@ seedDemo();
renderEmployees();
renderSiz();
renderWarehouse();
+renderNorms();
renderIssuances();
renderControl();
renderReports();