@opengis/gis 0.2.182 → 0.2.183

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/{CardIcon-g4POnzwa.js → CardIcon-CKcvisBB.js} +1 -1
  2. package/dist/{EntityTablePage-B7K_hSYY.js → EntityTablePage-Dj4Ysqkw.js} +1 -1
  3. package/dist/{ExtentOutlineLayer.vue_vue_type_script_setup_true_lang-BcSv7m_s.js → ExtentOutlineLayer.vue_vue_type_script_setup_true_lang-zNchIX_P.js} +1 -1
  4. package/dist/{MapSettings-C3hn2A7K.js → MapSettings-DH7BzcxB.js} +1 -1
  5. package/dist/{MapToolbarRight.vue_vue_type_script_setup_true_lang-Diu4LI2Z.js → MapToolbarRight.vue_vue_type_script_setup_true_lang-B5aTyoyH.js} +1 -1
  6. package/dist/{RastersTablePage-6Emo2MXj.js → RastersTablePage-BjgVVZDP.js} +1 -1
  7. package/dist/{cartocss-COLdNfzh.js → cartocss-B-5m_Jgg.js} +5 -5
  8. package/dist/{import-utils-DTDbjorg.js → import-utils-Bzs6QWW3.js} +1 -1
  9. package/dist/{index-Czv-aLH-.js → index-DhmYISKS.js} +10 -10
  10. package/dist/index.css +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/index.umd.cjs +3 -3
  13. package/dist/{raster-CB8C1210.js → raster-XFH5nm6L.js} +3 -3
  14. package/dist/{register-DkQ3WyMI.js → register-CNWivGIA.js} +3 -3
  15. package/dist/{service-BTKNJ8d_.js → service-CAelhYEL.js} +224 -215
  16. package/dist/{vs-datatable-C4NSnPIQ.js → vs-datatable-DkR1zed2.js} +3 -2
  17. package/module/gis/card/gis.metadata.table/index.yml +22 -22
  18. package/module/gis/card/gis.rasters.table/index.yml +11 -11
  19. package/module/gis/card/gis.registers.table/index.yml +23 -23
  20. package/module/gis/card/gis.services.table/index.yml +25 -25
  21. package/module/gis/cls/bool.yes_no.json +12 -12
  22. package/module/gis/cls/encoding.json +14 -14
  23. package/module/gis/cls/geom_type.json +14 -14
  24. package/module/gis/cls/gis.column_type.json +34 -34
  25. package/module/gis/cls/gis.column_view_type.json +26 -26
  26. package/module/gis/cls/gis.filter_type.json +22 -22
  27. package/module/gis/cls/language.json +10 -10
  28. package/module/gis/cls/meta.service_type.json +42 -42
  29. package/module/gis/cls/ogc.service.json +21 -21
  30. package/module/gis/cls/source_type.json +10 -10
  31. package/module/gis/cls/standarts.json +6 -6
  32. package/module/gis/cls/topic_category.json +106 -106
  33. package/module/gis/cls/update_frequency.json +29 -29
  34. package/module/gis/cls/view.json +21 -21
  35. package/module/gis/form/feedback.answer.form.json +10 -10
  36. package/module/gis/form/gis.cartocss.form.json +64 -64
  37. package/module/gis/form/gis.group_list.form.json +17 -17
  38. package/module/gis/form/gis.maps.form.json +61 -61
  39. package/module/gis/form/gis.metadata.form.json +240 -240
  40. package/module/gis/form/gis.metadata_new.form.json +195 -195
  41. package/module/gis/form/gis.ogc_service.form.json +51 -51
  42. package/module/gis/form/gis.rasters.form.json +76 -76
  43. package/module/gis/form/gis.registers_column.form.json +84 -84
  44. package/module/gis/form/gis.registers_filter.form.json +65 -65
  45. package/module/gis/form/gis.services.form.json +111 -111
  46. package/module/gis/form/gis.services_attributes.form.json +75 -75
  47. package/module/gis/form/gis.services_filter.form.json +65 -65
  48. package/module/gis/menu.json +43 -43
  49. package/module/gis/select/admin.cls.name.sql +9 -9
  50. package/module/gis/select/layer_list.sql +7 -7
  51. package/module/gis/select/layer_list_text.sql +7 -7
  52. package/module/gis/select/pg.column_name.geometry.sql +12 -12
  53. package/module/gis/select/pg.columns.parent.sql +6 -6
  54. package/module/gis/select/pg.table_name.sql +17 -17
  55. package/module/gis/select/service_id.sql +1 -1
  56. package/module/gis/select/srid.sql +7 -7
  57. package/module/gis/table/gis.cartocss.table.json +74 -74
  58. package/module/gis/table/gis.group_list.table.json +58 -58
  59. package/module/gis/table/gis.maps.table.json +110 -110
  60. package/module/gis/table/gis.metadata.table.json +70 -70
  61. package/module/gis/table/gis.metadata_new.table.json +137 -137
  62. package/module/gis/table/gis.ogc_service.table.json +100 -100
  63. package/module/gis/table/gis.rasters.table.json +113 -113
  64. package/module/gis/table/gis.registers.table.json +143 -143
  65. package/module/gis/table/gis.services.table.json +120 -120
  66. package/module/gis/table/site.gis.registers.table.json +88 -88
  67. package/module/gis/table/site.gis.services.table.json +106 -106
  68. package/module/gis/templates/ISO19136_2017_gml_template.xml +330 -330
  69. package/module/gis/tokens.yml +5 -5
  70. package/module/permissions/form/permissions.users.form.json +151 -151
  71. package/module/permissions/table/gis.permissions.table.json +97 -97
  72. package/package.json +1 -1
  73. package/server/routes/map/functions/gis.icon.js +1 -1
  74. package/server/routes/map/functions/mapFormat.js +11 -2
  75. package/server/routes/services/functions/get.services.js +5 -0
@@ -1,5 +1,5 @@
1
- maps: { table: 'gis.maps.table'}
2
- metadata: { table: 'gis.metadata.table' }
3
- rasters: { table: 'gis.rasters.table' }
4
- registers: { table: 'gis.registers.table' }
5
- services: { table: 'gis.services.table'}
1
+ maps: { table: 'gis.maps.table'}
2
+ metadata: { table: 'gis.metadata.table' }
3
+ rasters: { table: 'gis.rasters.table' }
4
+ registers: { table: 'gis.registers.table' }
5
+ services: { table: 'gis.services.table'}
@@ -1,152 +1,152 @@
1
- {
2
- "schema": {
3
- "d1": {
4
- "type": "HTML",
5
- "title": false,
6
- "text": "<h4 class='text-center'>Акаунт</h4><br>",
7
- "col": 12
8
- },
9
- "user_name": {
10
- "type": "Text",
11
- "validators": [
12
- "required"
13
- ],
14
- "ua": "Ім'я",
15
- "i": "Вноситься ім'я користувача, що буде відображатися у системі",
16
- "ru": "Имя"
17
- },
18
- "sur_name": {
19
- "type": "Text",
20
- "validators": [
21
- "required"
22
- ],
23
- "ua": "Прізвище",
24
- "i": "Вноситься прізвище користувача, що буде відображатися у системі",
25
- "ru": "Фамилия"
26
- },
27
- "father_name": {
28
- "ua": "По-батькові",
29
- "type": "text"
30
- },
31
- "phone": {
32
- "type": "MarkedText",
33
- "mask": "+389999999999",
34
- "id": "1",
35
- "ua": "Телефон",
36
- "i": "Вноситься телефон користувача",
37
- "ru": "Телефон"
38
- },
39
- "email": {
40
- "type": "Email",
41
- "ua": "E-mail",
42
- "i": "Вноситься електронна адреса користувача",
43
- "ru": "E-mail",
44
- "validators": [
45
- "email"
46
- ]
47
- },
48
- "d2": {
49
- "type": "HTML",
50
- "title": false,
51
- "text": "<h4 class='text-center'>Логін / Пароль</h4><br>",
52
- "hidden": true,
53
- "col": 12
54
- },
55
- "login": {
56
- "type": "Text",
57
- "ua": "Логін",
58
- "i": "Вноситься довільний логін користувача латинськими літерами, що буде використовуватися для входу в систему",
59
- "ru": "Логин",
60
- "validators": [
61
- "required"
62
- ]
63
- },
64
- "password": {
65
- "type": "Password",
66
- "validators": [
67
- "required",
68
- {
69
- "type": "regexp",
70
- "regexp": "^.{8,}$",
71
- "flags": "gm",
72
- "message": "Пароль повинен бути більше 8 символів"
73
- }
74
- ],
75
- "ua": "Пароль",
76
- "i": "Вноситься пароль, що буде використовуватися для входу в систему (рекомендоване використання складних паролів)",
77
- "ru": "Пароль"
78
- },
79
- "d3": {
80
- "type": "HTML",
81
- "title": false,
82
- "text": "<h4 class='text-center'>Доступ</h4><br>",
83
- "col": 12
84
- },
85
- "user_type": {
86
- "type": "Autocomplete",
87
- "data": "users.user_type",
88
- "default": "regular",
89
- "ua": "Тип користувача"
90
- },
91
- "enabled": {
92
- "type": "Switcher",
93
- "ua": "Off/On",
94
- "help": "Off - користувач вимкнутий; On - Користувач увімкнутий"
95
- },
96
- "readonly": {
97
- "type": "Switcher",
98
- "ua": "Read Only",
99
- "help": "On - У користувача усі інтерфейси в режимі перегляду, без редагування"
100
- },
101
- "d4": {
102
- "type": "HTML",
103
- "title": false,
104
- "text": "<h4 class='text-center'>Профіль</h4><br>",
105
- "col": 12
106
- },
107
- "user_rnokpp": {
108
- "type": "MarkedText",
109
- "mask": "9999999999",
110
- "ua": "РНОКПП",
111
- "col": 6,
112
- "validators": [
113
- {
114
- "type": "regexp",
115
- "regexp": "^([0-9]{10,10})$",
116
- "flags": "g",
117
- "message": "Лише цифри, 10 символів"
118
- }
119
- ]
120
- },
121
- "organ_edrpou": {
122
- "type": "MarkedText",
123
- "mask": "99999999",
124
- "ua": "Код ЄДРПОУ",
125
- "col": 6,
126
- "validators": [
127
- {
128
- "type": "regexp",
129
- "regexp": "^([0-9]{8,8})$",
130
- "flags": "g",
131
- "message": "Лише цифри, 8 символів"
132
- }
133
- ]
134
- },
135
- "unzr": {
136
- "type": "MarkedText",
137
- "mask": "99999999-9999",
138
- "ua": "УНЗР",
139
- "col": 6,
140
- "i": "Унікальний номер запису в Єдиному державному демографічному реєстрі"
141
- },
142
- "avatar": {
143
- "type": "File",
144
- "ua": "Аватар",
145
- "i": "Додається зображення, що буде відображено в системі у якості аватара цього користувача",
146
- "ru": "Аватар",
147
- "dir": "avatar"
148
- }
149
- },
150
- "label_style": "vertical",
151
- "width": 900
1
+ {
2
+ "schema": {
3
+ "d1": {
4
+ "type": "HTML",
5
+ "title": false,
6
+ "text": "<h4 class='text-center'>Акаунт</h4><br>",
7
+ "col": 12
8
+ },
9
+ "user_name": {
10
+ "type": "Text",
11
+ "validators": [
12
+ "required"
13
+ ],
14
+ "ua": "Ім'я",
15
+ "i": "Вноситься ім'я користувача, що буде відображатися у системі",
16
+ "ru": "Имя"
17
+ },
18
+ "sur_name": {
19
+ "type": "Text",
20
+ "validators": [
21
+ "required"
22
+ ],
23
+ "ua": "Прізвище",
24
+ "i": "Вноситься прізвище користувача, що буде відображатися у системі",
25
+ "ru": "Фамилия"
26
+ },
27
+ "father_name": {
28
+ "ua": "По-батькові",
29
+ "type": "text"
30
+ },
31
+ "phone": {
32
+ "type": "MarkedText",
33
+ "mask": "+389999999999",
34
+ "id": "1",
35
+ "ua": "Телефон",
36
+ "i": "Вноситься телефон користувача",
37
+ "ru": "Телефон"
38
+ },
39
+ "email": {
40
+ "type": "Email",
41
+ "ua": "E-mail",
42
+ "i": "Вноситься електронна адреса користувача",
43
+ "ru": "E-mail",
44
+ "validators": [
45
+ "email"
46
+ ]
47
+ },
48
+ "d2": {
49
+ "type": "HTML",
50
+ "title": false,
51
+ "text": "<h4 class='text-center'>Логін / Пароль</h4><br>",
52
+ "hidden": true,
53
+ "col": 12
54
+ },
55
+ "login": {
56
+ "type": "Text",
57
+ "ua": "Логін",
58
+ "i": "Вноситься довільний логін користувача латинськими літерами, що буде використовуватися для входу в систему",
59
+ "ru": "Логин",
60
+ "validators": [
61
+ "required"
62
+ ]
63
+ },
64
+ "password": {
65
+ "type": "Password",
66
+ "validators": [
67
+ "required",
68
+ {
69
+ "type": "regexp",
70
+ "regexp": "^.{8,}$",
71
+ "flags": "gm",
72
+ "message": "Пароль повинен бути більше 8 символів"
73
+ }
74
+ ],
75
+ "ua": "Пароль",
76
+ "i": "Вноситься пароль, що буде використовуватися для входу в систему (рекомендоване використання складних паролів)",
77
+ "ru": "Пароль"
78
+ },
79
+ "d3": {
80
+ "type": "HTML",
81
+ "title": false,
82
+ "text": "<h4 class='text-center'>Доступ</h4><br>",
83
+ "col": 12
84
+ },
85
+ "user_type": {
86
+ "type": "Autocomplete",
87
+ "data": "users.user_type",
88
+ "default": "regular",
89
+ "ua": "Тип користувача"
90
+ },
91
+ "enabled": {
92
+ "type": "Switcher",
93
+ "ua": "Off/On",
94
+ "help": "Off - користувач вимкнутий; On - Користувач увімкнутий"
95
+ },
96
+ "readonly": {
97
+ "type": "Switcher",
98
+ "ua": "Read Only",
99
+ "help": "On - У користувача усі інтерфейси в режимі перегляду, без редагування"
100
+ },
101
+ "d4": {
102
+ "type": "HTML",
103
+ "title": false,
104
+ "text": "<h4 class='text-center'>Профіль</h4><br>",
105
+ "col": 12
106
+ },
107
+ "user_rnokpp": {
108
+ "type": "MarkedText",
109
+ "mask": "9999999999",
110
+ "ua": "РНОКПП",
111
+ "col": 6,
112
+ "validators": [
113
+ {
114
+ "type": "regexp",
115
+ "regexp": "^([0-9]{10,10})$",
116
+ "flags": "g",
117
+ "message": "Лише цифри, 10 символів"
118
+ }
119
+ ]
120
+ },
121
+ "organ_edrpou": {
122
+ "type": "MarkedText",
123
+ "mask": "99999999",
124
+ "ua": "Код ЄДРПОУ",
125
+ "col": 6,
126
+ "validators": [
127
+ {
128
+ "type": "regexp",
129
+ "regexp": "^([0-9]{8,8})$",
130
+ "flags": "g",
131
+ "message": "Лише цифри, 8 символів"
132
+ }
133
+ ]
134
+ },
135
+ "unzr": {
136
+ "type": "MarkedText",
137
+ "mask": "99999999-9999",
138
+ "ua": "УНЗР",
139
+ "col": 6,
140
+ "i": "Унікальний номер запису в Єдиному державному демографічному реєстрі"
141
+ },
142
+ "avatar": {
143
+ "type": "File",
144
+ "ua": "Аватар",
145
+ "i": "Додається зображення, що буде відображено в системі у якості аватара цього користувача",
146
+ "ru": "Аватар",
147
+ "dir": "avatar"
148
+ }
149
+ },
150
+ "label_style": "vertical",
151
+ "width": 900
152
152
  }
@@ -1,98 +1,98 @@
1
- {
2
- "key": "uid",
3
- "table": "admin.users",
4
- "actions": [
5
- "add",
6
- "edit"
7
- ],
8
- "controls": [
9
- "search",
10
- "list",
11
- "add"
12
- ],
13
- "order": "cdate desc",
14
- "form": "admin.users.form",
15
- "meta": {
16
- "title": "full_name",
17
- "search": "user_name,sur_name,father_name,login,email,phone"
18
- },
19
- "title": "Доступи до шарів",
20
- "sql": [
21
- {
22
- "sql": "select count(*) as count_layers from gis.services_users_rel where user_id=t.uid",
23
- "name": "count_layers_sql"
24
- },
25
- {
26
- "sql": "select concat(user_name, ' ', sur_name, ' ', father_name, ' ') as full_name",
27
- "name": "full_name_sql"
28
- }
29
- ],
30
- "columns": [
31
- {
32
- "ua": "ПІБ",
33
- "name": "full_name",
34
- "format": "text",
35
- "link": "gis.permissions/{id}"
36
- },
37
- {
38
- "ua": "Логін",
39
- "name": "login",
40
- "hidden": true,
41
- "format": "text"
42
- },
43
- {
44
- "ua": "Email",
45
- "name": "email",
46
- "format": "text"
47
- },
48
- {
49
- "ua": "РНОКПП",
50
- "name": "user_rnokpp",
51
- "format": "text"
52
- },
53
- {
54
- "ua": "Кількість шарів в доступі",
55
- "name": "count_layers",
56
- "format": "text"
57
- },
58
- {
59
- "name": "cdate",
60
- "ua": "Створено",
61
- "format": "date"
62
- },
63
- {
64
- "name": "enabled",
65
- "ua": "On / Off",
66
- "data": "yes_no",
67
- "format": "check"
68
- }
69
- ],
70
- "filters": [
71
- {
72
- "ua": "РНОКПП",
73
- "name": "user_rnokpp",
74
- "type": "Text"
75
- },
76
- {
77
- "ua": "Email",
78
- "name": "email",
79
- "type": "Text"
80
- },
81
- {
82
- "ua": "Кількість шарів в доступі",
83
- "name": "count_layers",
84
- "type": "Range"
85
- },
86
- {
87
- "name": "enabled",
88
- "type": "Check",
89
- "data": "yes_no",
90
- "ua": "On / Off"
91
- },
92
- {
93
- "ua": "Дата створення",
94
- "name": "cdate",
95
- "type": "Date"
96
- }
97
- ]
1
+ {
2
+ "key": "uid",
3
+ "table": "admin.users",
4
+ "actions": [
5
+ "add",
6
+ "edit"
7
+ ],
8
+ "controls": [
9
+ "search",
10
+ "list",
11
+ "add"
12
+ ],
13
+ "order": "cdate desc",
14
+ "form": "admin.users.form",
15
+ "meta": {
16
+ "title": "full_name",
17
+ "search": "user_name,sur_name,father_name,login,email,phone"
18
+ },
19
+ "title": "Доступи до шарів",
20
+ "sql": [
21
+ {
22
+ "sql": "select count(*) as count_layers from gis.services_users_rel where user_id=t.uid",
23
+ "name": "count_layers_sql"
24
+ },
25
+ {
26
+ "sql": "select concat(user_name, ' ', sur_name, ' ', father_name, ' ') as full_name",
27
+ "name": "full_name_sql"
28
+ }
29
+ ],
30
+ "columns": [
31
+ {
32
+ "ua": "ПІБ",
33
+ "name": "full_name",
34
+ "format": "text",
35
+ "link": "gis.permissions/{id}"
36
+ },
37
+ {
38
+ "ua": "Логін",
39
+ "name": "login",
40
+ "hidden": true,
41
+ "format": "text"
42
+ },
43
+ {
44
+ "ua": "Email",
45
+ "name": "email",
46
+ "format": "text"
47
+ },
48
+ {
49
+ "ua": "РНОКПП",
50
+ "name": "user_rnokpp",
51
+ "format": "text"
52
+ },
53
+ {
54
+ "ua": "Кількість шарів в доступі",
55
+ "name": "count_layers",
56
+ "format": "text"
57
+ },
58
+ {
59
+ "name": "cdate",
60
+ "ua": "Створено",
61
+ "format": "date"
62
+ },
63
+ {
64
+ "name": "enabled",
65
+ "ua": "On / Off",
66
+ "data": "yes_no",
67
+ "format": "check"
68
+ }
69
+ ],
70
+ "filters": [
71
+ {
72
+ "ua": "РНОКПП",
73
+ "name": "user_rnokpp",
74
+ "type": "Text"
75
+ },
76
+ {
77
+ "ua": "Email",
78
+ "name": "email",
79
+ "type": "Text"
80
+ },
81
+ {
82
+ "ua": "Кількість шарів в доступі",
83
+ "name": "count_layers",
84
+ "type": "Range"
85
+ },
86
+ {
87
+ "name": "enabled",
88
+ "type": "Check",
89
+ "data": "yes_no",
90
+ "ua": "On / Off"
91
+ },
92
+ {
93
+ "ua": "Дата створення",
94
+ "name": "cdate",
95
+ "type": "Date"
96
+ }
97
+ ]
98
98
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/gis",
3
- "version": "0.2.182",
3
+ "version": "0.2.183",
4
4
  "type": "module",
5
5
  "author": "Softpro",
6
6
  "main": "./dist/index.js",
@@ -6,7 +6,7 @@ import * as solidIcons from '@fortawesome/free-solid-svg-icons';
6
6
  import sharp from 'sharp';
7
7
  import { BadRequestError, NotFoundError } from '@opengis/fastify-table/errors.js';
8
8
 
9
- const iconFilenamePattern = /^([a-z][a-z0-9]*)-(s|m|l|xl)-(fa|ar)-([a-z0-9-]+)\+([a-f0-9]{3,8})\.(png|svg)$/i;
9
+ const iconFilenamePattern = /^([a-z][a-z0-9]*)-(s|m|l|xl)-(fa|ar)-([\p{L}\p{N}-]+)\+([a-f0-9]{3,8})\.(png|svg)$/iu;
10
10
  const validColorLengths = new Set([3, 4, 6, 8]);
11
11
 
12
12
  const iconSizeByCode = {
@@ -7,6 +7,8 @@ import {
7
7
  import listFiles from '../../../shared/listFiles.js';
8
8
 
9
9
  const excluded = ['created_by', 'created_at', 'updated_by', 'geom', 'id', 'uid', 'updated_at', 'cdate', 'editor_date', 'editor_id'];
10
+ /** Virtual card fields that are not DB columns (e.g. crm.files attachments). */
11
+ const virtualColumns = new Set(['files']);
10
12
 
11
13
  function getLayerTableQuery({
12
14
  el, id, point, nogeom, radius,
@@ -123,7 +125,9 @@ async function getTableData({
123
125
 
124
126
  const columnNames = columns.map(col => col.name);
125
127
 
126
- const filteredColumns = columnNames.filter(name => !excluded.includes(name));
128
+ const filteredColumns = columnNames.filter(
129
+ (name) => !excluded.includes(name) && !virtualColumns.has(name),
130
+ );
127
131
 
128
132
  const rows = await pg.query(
129
133
  `SELECT ${pk} as "id", ${!nogeom ? 'st_asgeojson(geom)::json as geom, geom::box2d as box2d' : ''} ${!nogeom && filteredColumns?.length > 0 ? ',' : ''} ${filteredColumns.map((n) => `"${n}"`).join(', ')} FROM ${tname} WHERE ${pk} = $1`,
@@ -191,9 +195,14 @@ export default async function mapFormat({ layer, id, lat, lng, nogeom, radius =
191
195
  const fullRow = rows[0];
192
196
 
193
197
  const files = rows.length ? await listFiles({ /*entityType: tname,*/ entityId: fullRow.id }, { pg }) : [];
198
+ // URL-рядки для існуючого format: "file" у картці (file_path — шлях SoftPro / S3)
199
+ const fileUrls = files
200
+ .map((file) => (typeof file?.file_path === 'string' ? file.file_path.trim() : ''))
201
+ .filter(Boolean);
202
+ Object.assign(fullRow, { files: fileUrls });
194
203
 
195
204
  const result = [];
196
- const filtered = columns.filter(col => !excluded.includes(col.name));
205
+ const filtered = columns.filter(col => !excluded.includes(col.name) && !virtualColumns.has(col.name));
197
206
  for (let i = 0; i < filtered.length; i += 1) {
198
207
  const col = filtered[i];
199
208
 
@@ -52,6 +52,11 @@ export default async function getServices({ id }, pg = pgClients.client) {
52
52
 
53
53
  const fields = columns.map(({ name, dataTypeID, title }) => ({ name, type: columnType[pg.pgType?.[dataTypeID] || 'text'], label: title || name }));
54
54
 
55
+ // Віртуальне поле: файли з crm.files (не колонка таблиці source_path)
56
+ if (rows[0]?.source_path && pg.tlist?.includes('crm.files') && !fields.some((f) => f.name === 'files')) {
57
+ fields.push({ name: 'files', type: 'file', format: 'file', label: 'Файли' });
58
+ }
59
+
55
60
  const noCenterIds = rows.filter(row => !row.center && row.bbox).map(row => row.service_id);
56
61
 
57
62
  const centers = noCenterIds.length ? await pg.query('SELECT json_object_agg(service_id, st_pointonsurface(bbox)::json) FROM gis.services where service_id=any($1)', [noCenterIds].filter(Boolean)).then(r => r.rows?.[0]?.json_object_agg || {}) : {};