Добавлена функция сохранения истории обработки территорий
This commit is contained in:
32
api/controllers/generator.report.territories.controller.js
Normal file
32
api/controllers/generator.report.territories.controller.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
const getTable = require("../middleware/genReportTerritories");
|
||||||
|
|
||||||
|
class generatorReportTerritoriesController {
|
||||||
|
async getTable(req, res) {
|
||||||
|
if (req.possibilities.can_add_territory) {
|
||||||
|
let result = await getTable();
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
res.setHeader(
|
||||||
|
'Content-Disposition',
|
||||||
|
`attachment; filename*=UTF-8''${encodeURIComponent('Опрацювання_територій.xlsx')}`
|
||||||
|
);
|
||||||
|
res.setHeader(
|
||||||
|
'Content-Type',
|
||||||
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
|
);
|
||||||
|
return res.status(200).send(result);
|
||||||
|
} else {
|
||||||
|
return res
|
||||||
|
.status(404)
|
||||||
|
.send({ message: 'Table creation error.' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return res
|
||||||
|
.status(403)
|
||||||
|
.send({ message: 'The user does not have enough rights.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = new generatorReportTerritoriesController();
|
||||||
241
api/middleware/genReportTerritories.js
Normal file
241
api/middleware/genReportTerritories.js
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
const db = require("../config/db");
|
||||||
|
const ExcelJS = require('exceljs');
|
||||||
|
|
||||||
|
async function getTable() {
|
||||||
|
const workbook = new ExcelJS.Workbook();
|
||||||
|
const sheet = workbook.addWorksheet('Опрацювання територій');
|
||||||
|
|
||||||
|
// Обычная тонкая рамка
|
||||||
|
function setThinBorder(cell) {
|
||||||
|
cell.border = {
|
||||||
|
top: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
|
left: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
|
bottom: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
|
right: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Жирные рамки для Вісників
|
||||||
|
function setTopCellBorder(cell) {
|
||||||
|
cell.border = {
|
||||||
|
top: { style: 'thick', color: { argb: 'FF000000' } },
|
||||||
|
left: { style: 'thick', color: { argb: 'FF000000' } },
|
||||||
|
right: { style: 'thick', color: { argb: 'FF000000' } },
|
||||||
|
bottom: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStartColBorder(cell) {
|
||||||
|
cell.border = {
|
||||||
|
top: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
|
left: { style: 'thick', color: { argb: 'FF000000' } },
|
||||||
|
bottom: { style: 'thick', color: { argb: 'FF000000' } },
|
||||||
|
right: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setEndColBorder(cell) {
|
||||||
|
cell.border = {
|
||||||
|
top: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
|
left: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
|
bottom: { style: 'thick', color: { argb: 'FF000000' } },
|
||||||
|
right: { style: 'thick', color: { argb: 'FF000000' } },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
sheet.addRow([]);
|
||||||
|
sheet.addRow([]);
|
||||||
|
|
||||||
|
// № объекта
|
||||||
|
sheet.getCell('A1').value = '№';
|
||||||
|
sheet.mergeCells('A1:A2');
|
||||||
|
sheet.getCell('A1').alignment = { vertical: 'middle', horizontal: 'center' };
|
||||||
|
sheet.getCell('A1').font = { bold: true };
|
||||||
|
setThinBorder(sheet.getCell('A1'));
|
||||||
|
sheet.getColumn(1).width = 5;
|
||||||
|
|
||||||
|
// "Об’єкт"
|
||||||
|
sheet.getCell('B1').value = 'Територія';
|
||||||
|
sheet.mergeCells('B1:B2');
|
||||||
|
sheet.getCell('B1').alignment = { vertical: 'middle', horizontal: 'center' };
|
||||||
|
sheet.getCell('B1').font = { bold: true };
|
||||||
|
setThinBorder(sheet.getCell('B1'));
|
||||||
|
sheet.getColumn(2).width = 40;
|
||||||
|
|
||||||
|
// "Остання дата опрацювання"
|
||||||
|
sheet.getCell('C1').value = 'Остання дата опрацювання';
|
||||||
|
sheet.mergeCells('C1:C2');
|
||||||
|
sheet.getCell('C1').alignment = { vertical: 'middle', horizontal: 'center', wrapText: true };
|
||||||
|
sheet.getCell('C1').font = { bold: true };
|
||||||
|
setThinBorder(sheet.getCell('C1'));
|
||||||
|
sheet.getColumn(3).width = 20;
|
||||||
|
|
||||||
|
// Вісники
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
const startCol = 4 + i * 2;
|
||||||
|
const endCol = startCol + 1;
|
||||||
|
|
||||||
|
sheet.mergeCells(1, startCol, 1, endCol);
|
||||||
|
const topCell = sheet.getCell(1, startCol);
|
||||||
|
topCell.value = `Вісник (група) ${i + 1}`;
|
||||||
|
topCell.alignment = { horizontal: 'center', vertical: 'middle' };
|
||||||
|
topCell.font = { bold: true };
|
||||||
|
setThinBorder(topCell);
|
||||||
|
|
||||||
|
const recvCell = sheet.getCell(2, startCol);
|
||||||
|
recvCell.value = 'Дата отримання';
|
||||||
|
recvCell.alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
|
||||||
|
recvCell.font = { bold: true };
|
||||||
|
setThinBorder(recvCell);
|
||||||
|
|
||||||
|
const procCell = sheet.getCell(2, endCol);
|
||||||
|
procCell.value = 'Дата опрацювання';
|
||||||
|
procCell.alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
|
||||||
|
procCell.font = { bold: true };
|
||||||
|
setThinBorder(procCell);
|
||||||
|
|
||||||
|
sheet.getColumn(startCol).width = 16;
|
||||||
|
sheet.getColumn(endCol).width = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
const objects = await new Promise((resolve, reject) => {
|
||||||
|
db.all(`
|
||||||
|
SELECT
|
||||||
|
e.id as object_id,
|
||||||
|
e.entrance_number as number,
|
||||||
|
h.number as house_number,
|
||||||
|
h.title as house_title,
|
||||||
|
NULL as settlement,
|
||||||
|
'entrance' as type
|
||||||
|
FROM entrance e
|
||||||
|
JOIN house h ON e.house_id = h.id
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
hm.id as object_id,
|
||||||
|
hm.number as number,
|
||||||
|
NULL as house_number,
|
||||||
|
hm.title as house_title,
|
||||||
|
hm.settlement,
|
||||||
|
'homestead' as type
|
||||||
|
FROM homestead hm
|
||||||
|
`, (err, rows) => {
|
||||||
|
if (err) return reject(err);
|
||||||
|
resolve(rows);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let currentRow = 3;
|
||||||
|
let objIndex = 1;
|
||||||
|
|
||||||
|
for (const obj of objects) {
|
||||||
|
const table = obj.type === 'entrance' ? 'entrance_history' : 'homestead_history';
|
||||||
|
const idField = obj.type === 'entrance' ? 'entrance_id' : 'homestead_id';
|
||||||
|
|
||||||
|
const history = await new Promise((resolve, reject) => {
|
||||||
|
db.all(`
|
||||||
|
SELECT date_start, date_end, name, group_id
|
||||||
|
FROM ${table}
|
||||||
|
WHERE ${idField} = ?
|
||||||
|
ORDER BY date_start DESC
|
||||||
|
LIMIT 4
|
||||||
|
`, [obj.object_id], (err, rows) => {
|
||||||
|
if (err) return reject(err);
|
||||||
|
resolve(rows);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// № объекта
|
||||||
|
const numCell = sheet.getCell(currentRow, 1);
|
||||||
|
numCell.value = objIndex;
|
||||||
|
numCell.alignment = { vertical: 'middle', horizontal: 'center' };
|
||||||
|
setThinBorder(numCell);
|
||||||
|
sheet.mergeCells(currentRow, 1, currentRow + 1, 1);
|
||||||
|
|
||||||
|
// Название объекта
|
||||||
|
let objectName;
|
||||||
|
if (obj.type === 'homestead' && obj.settlement) {
|
||||||
|
objectName = `(${obj.settlement}) ${obj.house_title} ${obj.number}`;
|
||||||
|
} else if (obj.type === 'homestead') {
|
||||||
|
objectName = `${obj.house_title} ${obj.number}`;
|
||||||
|
} else {
|
||||||
|
objectName = `${obj.house_title} ${obj.house_number} (П. ${obj.number + 1})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const objCell = sheet.getCell(currentRow, 2);
|
||||||
|
objCell.value = objectName;
|
||||||
|
sheet.mergeCells(currentRow, 2, currentRow + 1, 2);
|
||||||
|
objCell.alignment = { vertical: 'middle', horizontal: 'left' };
|
||||||
|
setThinBorder(objCell);
|
||||||
|
|
||||||
|
// Последняя дата обработки
|
||||||
|
let lastDate = null;
|
||||||
|
for (const h of history) {
|
||||||
|
if (h.date_end && h.date_end !== 0) {
|
||||||
|
lastDate = new Date(h.date_end);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const lastDateCell = sheet.getCell(currentRow, 3);
|
||||||
|
lastDateCell.value = lastDate;
|
||||||
|
lastDateCell.numFmt = 'dd.mm.yyyy';
|
||||||
|
sheet.mergeCells(currentRow, 3, currentRow + 1, 3);
|
||||||
|
lastDateCell.alignment = { vertical: 'middle', horizontal: 'center' };
|
||||||
|
setThinBorder(lastDateCell);
|
||||||
|
|
||||||
|
// История Вісників
|
||||||
|
history.forEach((h, idx) => {
|
||||||
|
const startCol = 4 + idx * 2;
|
||||||
|
const endCol = startCol + 1;
|
||||||
|
|
||||||
|
let name = h.name;
|
||||||
|
if (h.name === 'Групова') name = `${h.name} (${h.group_id})`;
|
||||||
|
|
||||||
|
// Верхняя ячейка имени
|
||||||
|
const nameCell = sheet.getCell(currentRow, startCol);
|
||||||
|
sheet.mergeCells(currentRow, startCol, currentRow, endCol);
|
||||||
|
nameCell.value = name;
|
||||||
|
nameCell.alignment = { horizontal: 'center' };
|
||||||
|
setTopCellBorder(nameCell);
|
||||||
|
|
||||||
|
// Дата получения
|
||||||
|
const startDate = h.date_start ? new Date(h.date_start) : null;
|
||||||
|
const recvCell = sheet.getCell(currentRow + 1, startCol);
|
||||||
|
recvCell.value = startDate;
|
||||||
|
recvCell.numFmt = 'dd.mm.yyyy';
|
||||||
|
recvCell.alignment = { horizontal: 'center' };
|
||||||
|
setStartColBorder(recvCell);
|
||||||
|
|
||||||
|
// Дата опрацювання
|
||||||
|
const endDate = h.date_end ? new Date(h.date_end) : null;
|
||||||
|
const procCell = sheet.getCell(currentRow + 1, endCol);
|
||||||
|
procCell.value = endDate;
|
||||||
|
procCell.numFmt = 'dd.mm.yyyy';
|
||||||
|
procCell.alignment = { horizontal: 'center' };
|
||||||
|
setEndColBorder(procCell);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Чётное затемнение только для блока данных
|
||||||
|
if (objIndex % 2 === 0) {
|
||||||
|
for (let col = 1; col <= 11; col++) {
|
||||||
|
sheet.getCell(currentRow, col).fill = {
|
||||||
|
type: 'pattern',
|
||||||
|
pattern: 'solid',
|
||||||
|
fgColor: { argb: 'FFEFEFEF' },
|
||||||
|
};
|
||||||
|
sheet.getCell(currentRow + 1, col).fill = {
|
||||||
|
type: 'pattern',
|
||||||
|
pattern: 'solid',
|
||||||
|
fgColor: { argb: 'FFEFEFEF' },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentRow += 2;
|
||||||
|
objIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = await workbook.xlsx.writeBuffer();
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = getTable;
|
||||||
1025
api/package-lock.json
generated
1025
api/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@
|
|||||||
"puppeteer": "^24.4.0",
|
"puppeteer": "^24.4.0",
|
||||||
"sharp": "^0.33.5",
|
"sharp": "^0.33.5",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
"web-push": "^3.6.7"
|
"web-push": "^3.6.7",
|
||||||
|
"exceljs": "^4.4.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
api/routes/generator.report.territories.routes.js
Normal file
10
api/routes/generator.report.territories.routes.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const generatorReportTerritoriesController = require('../controllers/generator.report.territories.controller');
|
||||||
|
const authenticate = require("../middleware/auth");
|
||||||
|
|
||||||
|
router
|
||||||
|
.route('/')
|
||||||
|
.get(authenticate, generatorReportTerritoriesController.getTable);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -15,6 +15,7 @@ const standRoutes = require('./stand.routes');
|
|||||||
const pushRoutes = require('./push.routes');
|
const pushRoutes = require('./push.routes');
|
||||||
|
|
||||||
const generatorCardsRoutes = require('./generator.cards.routes');
|
const generatorCardsRoutes = require('./generator.cards.routes');
|
||||||
|
const generatorReportTerritoriesRoutes = require('./generator.report.territories.routes');
|
||||||
|
|
||||||
router.use('/auth', authRoutes);
|
router.use('/auth', authRoutes);
|
||||||
router.use('/sheeps?', sheepsRoutes);
|
router.use('/sheeps?', sheepsRoutes);
|
||||||
@@ -30,5 +31,6 @@ router.use('/stand', standRoutes);
|
|||||||
router.use('/push', pushRoutes);
|
router.use('/push', pushRoutes);
|
||||||
|
|
||||||
router.use('/generator/cards', generatorCardsRoutes);
|
router.use('/generator/cards', generatorCardsRoutes);
|
||||||
|
router.use('/generator/report/territories', generatorReportTerritoriesRoutes);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
@@ -11,51 +11,56 @@ server {
|
|||||||
index index.html;
|
index index.html;
|
||||||
error_page 404 /404.html;
|
error_page 404 /404.html;
|
||||||
|
|
||||||
location ~ /\.git {
|
# Захист службових файлів
|
||||||
|
location ~ /\.(git|env) {
|
||||||
deny all;
|
deny all;
|
||||||
}
|
}
|
||||||
|
|
||||||
location ~ /\.env {
|
# Загальні CORS-заголовки
|
||||||
deny all;
|
map $request_method $cors_preflight {
|
||||||
|
OPTIONS 1;
|
||||||
|
default 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# API
|
||||||
location /api/ {
|
location /api/ {
|
||||||
proxy_pass http://api:4000;
|
proxy_pass http://api:4000;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection "Upgrade";
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
|
||||||
if ($request_method = 'OPTIONS') {
|
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||||
add_header 'Access-Control-Allow-Origin' '*';
|
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
|
||||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
|
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
|
||||||
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
|
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
|
||||||
|
|
||||||
|
if ($cors_preflight) {
|
||||||
add_header 'Access-Control-Max-Age' 1728000;
|
add_header 'Access-Control-Max-Age' 1728000;
|
||||||
add_header 'Content-Type' 'text/plain; charset=utf-8';
|
add_header 'Content-Type' 'text/plain; charset=utf-8';
|
||||||
add_header 'Content-Length' 0;
|
add_header 'Content-Length' 0;
|
||||||
return 204;
|
return 204;
|
||||||
}
|
}
|
||||||
if ($request_method ~* 'GET|POST|PUT|DELETE') {
|
|
||||||
add_header 'Access-Control-Allow-Origin' '*' always;
|
|
||||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
|
|
||||||
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
|
|
||||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# WebSocket
|
||||||
location /ws {
|
location /ws {
|
||||||
proxy_pass http://ws:4001;
|
proxy_pass http://ws:4001;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection "Upgrade";
|
proxy_set_header Connection "upgrade";
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Frontend
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://web:4002;
|
proxy_pass http://web:4002;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection "Upgrade";
|
proxy_set_header Connection "upgrade";
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
|
||||||
if ($request_method = 'GET') {
|
if ($request_method = 'GET') {
|
||||||
|
|||||||
Binary file not shown.
@@ -2,122 +2,241 @@ const sqlite3 = require('sqlite3').verbose();
|
|||||||
const ExcelJS = require('exceljs');
|
const ExcelJS = require('exceljs');
|
||||||
|
|
||||||
async function exportData() {
|
async function exportData() {
|
||||||
const db = new sqlite3.Database('database.sqlite');
|
const db = new sqlite3.Database('database.sqlite');
|
||||||
const workbook = new ExcelJS.Workbook();
|
const workbook = new ExcelJS.Workbook();
|
||||||
|
const sheet = workbook.addWorksheet('Опрацювання територій');
|
||||||
|
|
||||||
// выгрузка подъездов
|
// Обычная тонкая рамка
|
||||||
await new Promise((resolve, reject) => {
|
function setThinBorder(cell) {
|
||||||
db.all(`
|
cell.border = {
|
||||||
SELECT
|
top: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
e.id as entrance_id,
|
left: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
e.entrance_number as entrance_title,
|
bottom: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
h.number as house_number,
|
right: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
h.title as house_title
|
};
|
||||||
FROM entrance e
|
}
|
||||||
JOIN house h ON e.house_id = h.id
|
|
||||||
`, async (err, entrances) => {
|
|
||||||
if (err) return reject(err);
|
|
||||||
|
|
||||||
for (const entrance of entrances) {
|
// Жирные рамки для Вісників
|
||||||
let title = String(entrance.house_title)
|
function setTopCellBorder(cell) {
|
||||||
.replace('вул. ', '')
|
cell.border = {
|
||||||
.replace('Проспект ', '');
|
top: { style: 'thick', color: { argb: 'FF000000' } },
|
||||||
|
left: { style: 'thick', color: { argb: 'FF000000' } },
|
||||||
|
right: { style: 'thick', color: { argb: 'FF000000' } },
|
||||||
|
bottom: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const sheet = workbook.addWorksheet(
|
function setStartColBorder(cell) {
|
||||||
`${title} ${entrance.house_number} ( П. ${entrance.entrance_title + 1} )`
|
cell.border = {
|
||||||
);
|
top: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
|
left: { style: 'thick', color: { argb: 'FF000000' } },
|
||||||
|
bottom: { style: 'thick', color: { argb: 'FF000000' } },
|
||||||
|
right: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
sheet.columns = [
|
function setEndColBorder(cell) {
|
||||||
{ header: 'ID', key: 'id', width: 10 },
|
cell.border = {
|
||||||
{ header: 'Вістник / Група', key: 'name', width: 30 },
|
top: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
{ header: 'Початок', key: 'start', width: 20 },
|
left: { style: 'thin', color: { argb: 'FF000000' } },
|
||||||
{ header: 'Кінець', key: 'end', width: 20 }
|
bottom: { style: 'thick', color: { argb: 'FF000000' } },
|
||||||
];
|
right: { style: 'thick', color: { argb: 'FF000000' } },
|
||||||
sheet.getColumn('start').numFmt = 'dd.mm.yyyy';
|
};
|
||||||
sheet.getColumn('end').numFmt = 'dd.mm.yyyy';
|
}
|
||||||
|
|
||||||
await new Promise((resolve2, reject2) => {
|
sheet.addRow([]);
|
||||||
db.all(`
|
sheet.addRow([]);
|
||||||
SELECT id, name, date_start, date_end, group_id
|
|
||||||
FROM entrance_history
|
|
||||||
WHERE entrance_id = ?
|
|
||||||
`, [entrance.entrance_id], (err, rows) => {
|
|
||||||
if (err) return reject2(err);
|
|
||||||
|
|
||||||
rows.forEach(row => {
|
// № объекта
|
||||||
const startDate = row.date_start ? new Date(row.date_start) : null;
|
sheet.getCell('A1').value = '№';
|
||||||
const endDate = row.date_end ? new Date(row.date_end) : null;
|
sheet.mergeCells('A1:A2');
|
||||||
|
sheet.getCell('A1').alignment = { vertical: 'middle', horizontal: 'center' };
|
||||||
|
sheet.getCell('A1').font = { bold: true };
|
||||||
|
setThinBorder(sheet.getCell('A1'));
|
||||||
|
sheet.getColumn(1).width = 5;
|
||||||
|
|
||||||
let name = row.name;
|
// "Об’єкт"
|
||||||
if (row.name === 'Групова') {
|
sheet.getCell('B1').value = 'Територія';
|
||||||
name = `${row.name} (${row.group_id})`;
|
sheet.mergeCells('B1:B2');
|
||||||
}
|
sheet.getCell('B1').alignment = { vertical: 'middle', horizontal: 'center' };
|
||||||
|
sheet.getCell('B1').font = { bold: true };
|
||||||
|
setThinBorder(sheet.getCell('B1'));
|
||||||
|
sheet.getColumn(2).width = 40;
|
||||||
|
|
||||||
sheet.addRow({ id: row.id, name, start: startDate, end: endDate });
|
// "Остання дата опрацювання"
|
||||||
});
|
sheet.getCell('C1').value = 'Остання дата опрацювання';
|
||||||
resolve2();
|
sheet.mergeCells('C1:C2');
|
||||||
});
|
sheet.getCell('C1').alignment = { vertical: 'middle', horizontal: 'center', wrapText: true };
|
||||||
|
sheet.getCell('C1').font = { bold: true };
|
||||||
|
setThinBorder(sheet.getCell('C1'));
|
||||||
|
sheet.getColumn(3).width = 20;
|
||||||
|
|
||||||
|
// Вісники
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
const startCol = 4 + i * 2;
|
||||||
|
const endCol = startCol + 1;
|
||||||
|
|
||||||
|
sheet.mergeCells(1, startCol, 1, endCol);
|
||||||
|
const topCell = sheet.getCell(1, startCol);
|
||||||
|
topCell.value = `Вісник (група) ${i + 1}`;
|
||||||
|
topCell.alignment = { horizontal: 'center', vertical: 'middle' };
|
||||||
|
topCell.font = { bold: true };
|
||||||
|
setThinBorder(topCell);
|
||||||
|
|
||||||
|
const recvCell = sheet.getCell(2, startCol);
|
||||||
|
recvCell.value = 'Дата отримання';
|
||||||
|
recvCell.alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
|
||||||
|
recvCell.font = { bold: true };
|
||||||
|
setThinBorder(recvCell);
|
||||||
|
|
||||||
|
const procCell = sheet.getCell(2, endCol);
|
||||||
|
procCell.value = 'Дата опрацювання';
|
||||||
|
procCell.alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
|
||||||
|
procCell.font = { bold: true };
|
||||||
|
setThinBorder(procCell);
|
||||||
|
|
||||||
|
sheet.getColumn(startCol).width = 16;
|
||||||
|
sheet.getColumn(endCol).width = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
const objects = await new Promise((resolve, reject) => {
|
||||||
|
db.all(`
|
||||||
|
SELECT
|
||||||
|
e.id as object_id,
|
||||||
|
e.entrance_number as number,
|
||||||
|
h.number as house_number,
|
||||||
|
h.title as house_title,
|
||||||
|
NULL as settlement,
|
||||||
|
'entrance' as type
|
||||||
|
FROM entrance e
|
||||||
|
JOIN house h ON e.house_id = h.id
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
hm.id as object_id,
|
||||||
|
hm.number as number,
|
||||||
|
NULL as house_number,
|
||||||
|
hm.title as house_title,
|
||||||
|
hm.settlement,
|
||||||
|
'homestead' as type
|
||||||
|
FROM homestead hm
|
||||||
|
`, (err, rows) => {
|
||||||
|
if (err) return reject(err);
|
||||||
|
resolve(rows);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// выгрузка участков
|
let currentRow = 3;
|
||||||
await new Promise((resolve, reject) => {
|
let objIndex = 1;
|
||||||
db.all(`
|
|
||||||
SELECT
|
|
||||||
homestead.id as homestead_id,
|
|
||||||
homestead.number as homestead_number,
|
|
||||||
homestead.title as homestead_title
|
|
||||||
FROM homestead
|
|
||||||
`, async (err, homesteads) => {
|
|
||||||
if (err) return reject(err);
|
|
||||||
|
|
||||||
for (const homestead of homesteads) {
|
for (const obj of objects) {
|
||||||
const sheet = workbook.addWorksheet(`${homestead.homestead_title} ${homestead.homestead_number}`);
|
const table = obj.type === 'entrance' ? 'entrance_history' : 'homestead_history';
|
||||||
|
const idField = obj.type === 'entrance' ? 'entrance_id' : 'homestead_id';
|
||||||
|
|
||||||
sheet.columns = [
|
const history = await new Promise((resolve, reject) => {
|
||||||
{ header: 'ID', key: 'id', width: 10 },
|
db.all(`
|
||||||
{ header: 'Вістник / Група', key: 'name', width: 30 },
|
SELECT date_start, date_end, name, group_id
|
||||||
{ header: 'Початок', key: 'start', width: 20 },
|
FROM ${table}
|
||||||
{ header: 'Кінець', key: 'end', width: 20 }
|
WHERE ${idField} = ?
|
||||||
];
|
ORDER BY date_start DESC
|
||||||
sheet.getColumn('start').numFmt = 'dd.mm.yyyy';
|
`, [obj.object_id], (err, rows) => {
|
||||||
sheet.getColumn('end').numFmt = 'dd.mm.yyyy';
|
if (err) return reject(err);
|
||||||
|
resolve(rows);
|
||||||
await new Promise((resolve2, reject2) => {
|
|
||||||
db.all(`
|
|
||||||
SELECT id, name, date_start, date_end, group_id
|
|
||||||
FROM homestead_history
|
|
||||||
WHERE homestead_id = ?
|
|
||||||
`, [homestead.homestead_id], (err, rows) => {
|
|
||||||
if (err) return reject2(err);
|
|
||||||
|
|
||||||
rows.forEach(row => {
|
|
||||||
const startDate = row.date_start ? new Date(row.date_start) : null;
|
|
||||||
const endDate = row.date_end ? new Date(row.date_end) : null;
|
|
||||||
|
|
||||||
let name = row.name;
|
|
||||||
if (row.name === 'Групова') {
|
|
||||||
name = `${row.name} (${row.group_id})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
sheet.addRow({ id: row.id, name, start: startDate, end: endDate });
|
|
||||||
});
|
});
|
||||||
resolve2();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// сохраняем один раз
|
// № объекта
|
||||||
await workbook.xlsx.writeFile('Території.xlsx');
|
const numCell = sheet.getCell(currentRow, 1);
|
||||||
console.log('Файл создан: entrances.xlsx');
|
numCell.value = objIndex;
|
||||||
|
numCell.alignment = { vertical: 'middle', horizontal: 'center' };
|
||||||
|
setThinBorder(numCell);
|
||||||
|
sheet.mergeCells(currentRow, 1, currentRow + 1, 1);
|
||||||
|
|
||||||
db.close();
|
// Название объекта
|
||||||
|
let objectName;
|
||||||
|
if (obj.type === 'homestead' && obj.settlement) {
|
||||||
|
objectName = `(${obj.settlement}) ${obj.house_title} ${obj.number}`;
|
||||||
|
} else if (obj.type === 'homestead') {
|
||||||
|
objectName = `${obj.house_title} ${obj.number}`;
|
||||||
|
} else {
|
||||||
|
objectName = `${obj.house_title} ${obj.house_number} (П. ${obj.number + 1})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const objCell = sheet.getCell(currentRow, 2);
|
||||||
|
objCell.value = objectName;
|
||||||
|
sheet.mergeCells(currentRow, 2, currentRow + 1, 2);
|
||||||
|
objCell.alignment = { vertical: 'middle', horizontal: 'left' };
|
||||||
|
setThinBorder(objCell);
|
||||||
|
|
||||||
|
// Последняя дата обработки
|
||||||
|
let lastDate = null;
|
||||||
|
for (const h of history) {
|
||||||
|
if (h.date_end && h.date_end !== 0) {
|
||||||
|
lastDate = new Date(h.date_end);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const lastDateCell = sheet.getCell(currentRow, 3);
|
||||||
|
lastDateCell.value = lastDate;
|
||||||
|
lastDateCell.numFmt = 'dd.mm.yyyy';
|
||||||
|
sheet.mergeCells(currentRow, 3, currentRow + 1, 3);
|
||||||
|
lastDateCell.alignment = { vertical: 'middle', horizontal: 'center' };
|
||||||
|
setThinBorder(lastDateCell);
|
||||||
|
|
||||||
|
// История Вісників
|
||||||
|
history.forEach((h, idx) => {
|
||||||
|
const startCol = 4 + idx * 2;
|
||||||
|
const endCol = startCol + 1;
|
||||||
|
|
||||||
|
let name = h.name;
|
||||||
|
if (h.name === 'Групова') name = `${h.name} (${h.group_id})`;
|
||||||
|
|
||||||
|
// Верхняя ячейка имени
|
||||||
|
const nameCell = sheet.getCell(currentRow, startCol);
|
||||||
|
sheet.mergeCells(currentRow, startCol, currentRow, endCol);
|
||||||
|
nameCell.value = name;
|
||||||
|
nameCell.alignment = { horizontal: 'center' };
|
||||||
|
setTopCellBorder(nameCell);
|
||||||
|
|
||||||
|
// Дата получения
|
||||||
|
const startDate = h.date_start ? new Date(h.date_start) : null;
|
||||||
|
const recvCell = sheet.getCell(currentRow + 1, startCol);
|
||||||
|
recvCell.value = startDate;
|
||||||
|
recvCell.numFmt = 'dd.mm.yyyy';
|
||||||
|
recvCell.alignment = { horizontal: 'center' };
|
||||||
|
setStartColBorder(recvCell);
|
||||||
|
|
||||||
|
// Дата опрацювання
|
||||||
|
const endDate = h.date_end ? new Date(h.date_end) : null;
|
||||||
|
const procCell = sheet.getCell(currentRow + 1, endCol);
|
||||||
|
procCell.value = endDate;
|
||||||
|
procCell.numFmt = 'dd.mm.yyyy';
|
||||||
|
procCell.alignment = { horizontal: 'center' };
|
||||||
|
setEndColBorder(procCell);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Чётное затемнение только для блока данных
|
||||||
|
if (objIndex % 2 === 0) {
|
||||||
|
for (let col = 1; col <= 11; col++) {
|
||||||
|
sheet.getCell(currentRow, col).fill = {
|
||||||
|
type: 'pattern',
|
||||||
|
pattern: 'solid',
|
||||||
|
fgColor: { argb: 'FFEFEFEF' },
|
||||||
|
};
|
||||||
|
sheet.getCell(currentRow + 1, col).fill = {
|
||||||
|
type: 'pattern',
|
||||||
|
pattern: 'solid',
|
||||||
|
fgColor: { argb: 'FFEFEFEF' },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentRow += 2;
|
||||||
|
objIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
await workbook.xlsx.writeFile('Опрацювання територій.xlsx');
|
||||||
|
console.log('Файл создан: Опрацювання територій.xlsx');
|
||||||
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
exportData();
|
exportData();
|
||||||
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
|||||||
const CONFIG = {
|
const CONFIG = {
|
||||||
"api": "https://sheep-service.com/api/",
|
"api": "https://test.sheep-service.com/api/",
|
||||||
"wss": "wss://sheep-service.com/ws"
|
"wss": "wss://test.sheep-service.com/ws"
|
||||||
}
|
}
|
||||||
@@ -142,15 +142,25 @@ const Card = {
|
|||||||
apt.status = msg.data.status;
|
apt.status = msg.data.status;
|
||||||
apt.description = msg.data.description;
|
apt.description = msg.data.description;
|
||||||
apt.updated_at = msg.data.updated_at;
|
apt.updated_at = msg.data.updated_at;
|
||||||
|
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
|
let date = new Date(msg.data.updated_at);
|
||||||
|
date.setMinutes(date.getMinutes() - date.getTimezoneOffset());
|
||||||
|
document.getElementById(`date_${id}`).value = date.toISOString().slice(0, 16);
|
||||||
|
|
||||||
} else if (msg.type === "apartment") {
|
} else if (msg.type === "apartment") {
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
|
|
||||||
document.getElementById(`card_${id}`).style = `background:${bg};color:${color};border:1px solid ${color}`;
|
|
||||||
el.value = msg.data.status;
|
|
||||||
el.style = `background:${bg};color:${color};border:1px solid ${color}`;
|
|
||||||
document.getElementById(`description_${id}`).value = msg.data.description;
|
|
||||||
document.getElementById(`date_text_${id}`).innerText = formattedDateTime(msg.data.updated_at);
|
document.getElementById(`date_text_${id}`).innerText = formattedDateTime(msg.data.updated_at);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!el) return;
|
||||||
|
document.getElementById(`card_${id}`).style = `background:${bg};color:${color};border:1px solid ${color}`;
|
||||||
|
el.value = msg.data.status;
|
||||||
|
el.style = `background:${bg};color:${color};border:1px solid ${color}`;
|
||||||
|
document.getElementById(`description_${id}`).value = msg.data.description;
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
messApartment({ number, id, update, time }) {
|
messApartment({ number, id, update, time }) {
|
||||||
@@ -205,6 +215,7 @@ const Card = {
|
|||||||
|
|
||||||
const statusEl = document.getElementById(`status_${id}`);
|
const statusEl = document.getElementById(`status_${id}`);
|
||||||
const descEl = document.getElementById(`description_${id}`);
|
const descEl = document.getElementById(`description_${id}`);
|
||||||
|
const dateEl = document.getElementById(`date_text_${id}`);
|
||||||
|
|
||||||
let date = () => {
|
let date = () => {
|
||||||
if (!update && !time) {
|
if (!update && !time) {
|
||||||
@@ -212,7 +223,8 @@ const Card = {
|
|||||||
} else if (update && !time) {
|
} else if (update && !time) {
|
||||||
return getTimeInSeconds();
|
return getTimeInSeconds();
|
||||||
} else if (update && time) {
|
} else if (update && time) {
|
||||||
return getTimeInSeconds(time);
|
const ts = new Date(time).getTime();
|
||||||
|
return getTimeInSeconds(ts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,6 +235,7 @@ const Card = {
|
|||||||
const [bg, color] = Card.color_status[apt.status];
|
const [bg, color] = Card.color_status[apt.status];
|
||||||
statusEl.style = `background:${bg};color:${color};border:1px solid ${color}`;
|
statusEl.style = `background:${bg};color:${color};border:1px solid ${color}`;
|
||||||
|
|
||||||
|
|
||||||
const message = {
|
const message = {
|
||||||
event: 'message',
|
event: 'message',
|
||||||
id: getTimeInSeconds(),
|
id: getTimeInSeconds(),
|
||||||
@@ -487,7 +500,7 @@ const Card = {
|
|||||||
// map_card.getZoom()
|
// map_card.getZoom()
|
||||||
|
|
||||||
// console.log(data.zoom);
|
// console.log(data.zoom);
|
||||||
|
|
||||||
|
|
||||||
Card.listBuilding = await this.loadAPI({ url: `${CONFIG.api}building/${homestead_id}` });
|
Card.listBuilding = await this.loadAPI({ url: `${CONFIG.api}building/${homestead_id}` });
|
||||||
|
|
||||||
@@ -496,42 +509,6 @@ const Card = {
|
|||||||
return labels.map((txt, i) => `<option value="${i}" ${i === selected ? "selected" : ""}>${txt}</option>`).join("");
|
return labels.map((txt, i) => `<option value="${i}" ${i === selected ? "selected" : ""}>${txt}</option>`).join("");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// for (let i = 0; i < Card.listBuilding.length; i++) {
|
|
||||||
// const element = Card.listBuilding[i];
|
|
||||||
// const [bg, color] = Card.color_status[element.status];
|
|
||||||
// const style = `background:${bg};color:${color};border:1px solid ${color}`;
|
|
||||||
|
|
||||||
// const div = document.createElement('div');
|
|
||||||
|
|
||||||
// const redDot = L.divIcon({
|
|
||||||
// className: "leaflet_drop",
|
|
||||||
// html: `<div id="redDot_${element.id}" style='background:${bg};border:2px solid ${color}'></div>`,
|
|
||||||
// iconSize: [16, 16],
|
|
||||||
// iconAnchor: [8, 8]
|
|
||||||
// });
|
|
||||||
|
|
||||||
// div.className = `card_info card_info_homestead`;
|
|
||||||
// div.id = `card_${element.id}`;
|
|
||||||
// div.style = style;
|
|
||||||
|
|
||||||
// div.innerHTML = `
|
|
||||||
// <div class="info">
|
|
||||||
// <select id="status_${element.id}" onchange="Card.cloud.messBuildings({id:${element.id},update:true})" style="${style}">
|
|
||||||
// ${statusOptions(element.status)}
|
|
||||||
// </select>
|
|
||||||
// <button id="date_${element.id}" onclick="Card.dateEditor.open({id:${element.id},updated_at:${element.updated_at}})">
|
|
||||||
// <p id="date_text_${element.id}">${formattedDateTime(element.updated_at)}</p>
|
|
||||||
// <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><path d="M 22.828125 3 C 22.316375 3 21.804562 3.1954375 21.414062 3.5859375 L 19 6 L 24 11 L 26.414062 8.5859375 C 27.195062 7.8049375 27.195062 6.5388125 26.414062 5.7578125 L 24.242188 3.5859375 C 23.851688 3.1954375 23.339875 3 22.828125 3 z M 17 8 L 5.2597656 19.740234 C 5.2597656 19.740234 6.1775313 19.658 6.5195312 20 C 6.8615312 20.342 6.58 22.58 7 23 C 7.42 23.42 9.6438906 23.124359 9.9628906 23.443359 C 10.281891 23.762359 10.259766 24.740234 10.259766 24.740234 L 22 13 L 17 8 z M 4 23 L 3.0566406 25.671875 A 1 1 0 0 0 3 26 A 1 1 0 0 0 4 27 A 1 1 0 0 0 4.328125 26.943359 A 1 1 0 0 0 4.3378906 26.939453 L 4.3632812 26.931641 A 1 1 0 0 0 4.3691406 26.927734 L 7 26 L 5.5 24.5 L 4 23 z"></path></svg>
|
|
||||||
// </button>
|
|
||||||
// </div>
|
|
||||||
// <textarea id="description_${element.id}" onchange="Card.cloud.messBuildings({id:${element.id}})" placeholder="Коротка нотатка.">${element.description || ""}</textarea>
|
|
||||||
// `;
|
|
||||||
|
|
||||||
// L.marker(element.geo, { icon: redDot }).addTo(map_card)
|
|
||||||
// .bindPopup(div);
|
|
||||||
// }
|
|
||||||
|
|
||||||
for (let i = 0; i < Card.listBuilding.length; i++) {
|
for (let i = 0; i < Card.listBuilding.length; i++) {
|
||||||
const element = Card.listBuilding[i];
|
const element = Card.listBuilding[i];
|
||||||
const [bg, color] = Card.color_status[element.status];
|
const [bg, color] = Card.color_status[element.status];
|
||||||
@@ -567,7 +544,7 @@ const Card = {
|
|||||||
<select id="status_${element.id}" onchange="Card.cloud.messBuildings({id:${element.id},update:true})" style="${style}">
|
<select id="status_${element.id}" onchange="Card.cloud.messBuildings({id:${element.id},update:true})" style="${style}">
|
||||||
${statusOptions(element.status)}
|
${statusOptions(element.status)}
|
||||||
</select>
|
</select>
|
||||||
<input type="datetime-local" id="date_${element.id}" placeholder="Дата" onchange="Card.cloud.messBuildings({id:${element.id}})" value="${date.toISOString().slice(0, 16)}">
|
<input type="datetime-local" id="date_${element.id}" placeholder="Дата" onchange="Card.cloud.messBuildings({id:${element.id},update:true, time: this.value })" value="${date.toISOString().slice(0, 16)}">
|
||||||
</div>
|
</div>
|
||||||
<textarea id="description_${element.id}" onchange="Card.cloud.messBuildings({id:${element.id}})" placeholder="Коротка нотатка.">${element.description || ""}</textarea>
|
<textarea id="description_${element.id}" onchange="Card.cloud.messBuildings({id:${element.id}})" placeholder="Коротка нотатка.">${element.description || ""}</textarea>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -13,6 +13,11 @@
|
|||||||
</svg>
|
</svg>
|
||||||
<span>Конструктор</span>
|
<span>Конструктор</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<button onclick="Territory.report()">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M13.13,2H5.958c-1.1,0-2,0.9-2,2v16c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V8.828c0-0.53-0.211-1.039-0.586-1.414l-4.828-4.828 C14.169,2.211,13.66,2,13.13,2z M11.255,17.711l-1.297-1.297l-1.251,1.293c-0.39,0.39-1.024,0.39-1.414,0l0,0 c-0.39-0.39-0.39-1.024,0-1.414l1.958-1.989c0.39-0.396,1.027-0.398,1.42-0.006l1.287,1.287l2.335-2.293 c0.39-0.39,1.024-0.39,1.414,0l0,0c0.39,0.39,0.39,1.024,0,1.414l-3.042,3.008C12.274,18.102,11.644,18.1,11.255,17.711z M12.958,9 V3.5l5.5,5.5H12.958z"/></svg>
|
||||||
|
<span>Звіт опрацьовання</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="list-controls">
|
<div class="list-controls">
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ const Territory = {
|
|||||||
const person = working
|
const person = working
|
||||||
? `<span>Територію опрацьовує:</span> <p>${element.history.name === 'Групова' ? 'Група ' + element.history.group_id : element.history.name}</p>`
|
? `<span>Територію опрацьовує:</span> <p>${element.history.name === 'Групова' ? 'Група ' + element.history.group_id : element.history.name}</p>`
|
||||||
: `<span>Територія не опрацьовується</span>`;
|
: `<span>Територія не опрацьовується</span>`;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<i style="background-image: url(https://sheep-service.com/cards/house/T${element.house.id}.webp);"></i>
|
<i style="background-image: url(https://sheep-service.com/cards/house/T${element.house.id}.webp);"></i>
|
||||||
@@ -248,5 +248,32 @@ const Territory = {
|
|||||||
|
|
||||||
Territory.house.setHTML();
|
Territory.house.setHTML();
|
||||||
Territory.homestead.setHTML();
|
Territory.homestead.setHTML();
|
||||||
|
},
|
||||||
|
report: async () => {
|
||||||
|
const uuid = localStorage.getItem("uuid");
|
||||||
|
const url = `${CONFIG.api}generator/report/territories`;
|
||||||
|
await fetch(url, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": uuid
|
||||||
|
}
|
||||||
|
}).then(response => {
|
||||||
|
if (!response.ok) throw new Error('Network response was not ok');
|
||||||
|
return response.blob();
|
||||||
|
})
|
||||||
|
.then(blob => {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
link.href = url;
|
||||||
|
link.download = 'report.xlsx';
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
link.remove();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('Fetch error:', err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user