odant-work 1.0.1 → 1.0.2
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.
- package/$server/$folder/$class/$structure/$server/handlers/pages/form/license/$handler/license.js +9 -6
- package/$server/$folder/$class/handlers/pages/form/object/$handler/class.js +27 -0
- package/$server/$folder/$class/handlers/pages/form/object/$handler/object.js +352 -0
- package/$server/$folder/$class/handlers/pages/form/objects/$handler/objects.js +39 -2
- package/$server/$folder/handlers/pages/form/$handler/form.js +427 -68
- package/$server/$folder/lib/tools/tools.js +9 -2
- package/MODULES/Register/REGISTER/$register/$folder/$class/$account/handlers/pages/form/postings/$handler/postings.js +388 -103
- package/MODULES/Register/REGISTER/$register/$folder/$class/$register/handlers/pages/form/journal/$handler/journal.js +84 -5
- package/MODULES/Register/REGISTER/$register/$folder/$class/$register/handlers/pages/form/sheet/$handler/sheet.js +84 -5
- package/data/store.json +3 -3
- package/oda/components/packets/packets.js +6 -0
- package/package.json +1 -1
- package/sources/data/api.js +180 -1
- package/sources/data/progress.md +37 -0
- package/sources/modules/license/index.js +7 -5
|
@@ -56,6 +56,74 @@ function _derivePacketsKey(from, to, fallback = '*') {
|
|
|
56
56
|
return fallback || '*';
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
function _daysInMonth(y, m) {
|
|
60
|
+
return new Date(y, m, 0).getDate();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function _weekBounds(dt) {
|
|
64
|
+
const dow = dt.getDay();
|
|
65
|
+
const diffToMon = dow === 0 ? -6 : 1 - dow;
|
|
66
|
+
const mon = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate() + diffToMon);
|
|
67
|
+
const sun = new Date(mon.getFullYear(), mon.getMonth(), mon.getDate() + 6);
|
|
68
|
+
return { from: _day(mon), to: _day(sun) };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function _boundsFromPacketsKey(key, defaultMode = 'month') {
|
|
72
|
+
const k = String(key || '').trim();
|
|
73
|
+
if (!k || k === '*')
|
|
74
|
+
return { from: '', to: '' };
|
|
75
|
+
if (k.startsWith('period:')) {
|
|
76
|
+
const [, s = '', e = ''] = k.split(':');
|
|
77
|
+
return { from: s, to: e };
|
|
78
|
+
}
|
|
79
|
+
if (k === 'now' || k.startsWith('now:')) {
|
|
80
|
+
const gran = k.startsWith('now:') ? k.slice(4) : defaultMode;
|
|
81
|
+
const now = new Date();
|
|
82
|
+
const y = now.getFullYear(), m = now.getMonth() + 1;
|
|
83
|
+
if (gran === 'day') {
|
|
84
|
+
const d = _day(now);
|
|
85
|
+
return { from: d, to: d };
|
|
86
|
+
}
|
|
87
|
+
if (gran === 'week')
|
|
88
|
+
return _weekBounds(now);
|
|
89
|
+
if (gran === 'quarter') {
|
|
90
|
+
const q = Math.floor((m - 1) / 3) + 1;
|
|
91
|
+
const m1 = String((q - 1) * 3 + 1).padStart(2, '0');
|
|
92
|
+
const m2 = (q - 1) * 3 + 3;
|
|
93
|
+
return { from: `${y}-${m1}-01`, to: `${y}-${String(m2).padStart(2, '0')}-${String(_daysInMonth(y, m2)).padStart(2, '0')}` };
|
|
94
|
+
}
|
|
95
|
+
if (gran === 'year')
|
|
96
|
+
return { from: `${y}-01-01`, to: `${y}-12-31` };
|
|
97
|
+
const ms = String(m).padStart(2, '0');
|
|
98
|
+
return { from: `${y}-${ms}-01`, to: `${y}-${ms}-${String(_daysInMonth(y, m)).padStart(2, '0')}` };
|
|
99
|
+
}
|
|
100
|
+
const clean = k.replace(/\*+$/, '');
|
|
101
|
+
if (/^\d{4}$/.test(clean))
|
|
102
|
+
return { from: `${clean}-01-01`, to: `${clean}-12-31` };
|
|
103
|
+
const qm = /^(\d{4})-q([1-4])$/i.exec(clean);
|
|
104
|
+
if (qm) {
|
|
105
|
+
const y = +qm[1], q = +qm[2];
|
|
106
|
+
const m1 = String((q - 1) * 3 + 1).padStart(2, '0');
|
|
107
|
+
const m2 = (q - 1) * 3 + 3;
|
|
108
|
+
return { from: `${y}-${m1}-01`, to: `${y}-${String(m2).padStart(2, '0')}-${String(_daysInMonth(y, m2)).padStart(2, '0')}` };
|
|
109
|
+
}
|
|
110
|
+
const mm = /^(\d{4})-(0[1-9]|1[0-2])$/.exec(clean);
|
|
111
|
+
if (mm) {
|
|
112
|
+
const y = +mm[1], m = +mm[2];
|
|
113
|
+
return { from: `${clean}-01`, to: `${clean}-${String(_daysInMonth(y, m)).padStart(2, '0')}` };
|
|
114
|
+
}
|
|
115
|
+
if (clean.startsWith('week:')) {
|
|
116
|
+
const monIso = clean.slice(5);
|
|
117
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(monIso)) {
|
|
118
|
+
const [y, m, d] = monIso.split('-').map(Number);
|
|
119
|
+
return _weekBounds(new Date(y, m - 1, d));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(clean))
|
|
123
|
+
return { from: clean, to: clean };
|
|
124
|
+
return { from: '', to: '' };
|
|
125
|
+
}
|
|
126
|
+
|
|
59
127
|
const money = v => (Number(v) ? Number(v).toLocaleString('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) : '');
|
|
60
128
|
const qtyFmt = v => (Number(v) ? Number(v).toLocaleString('ru-RU', { maximumFractionDigits: 4 }) : '');
|
|
61
129
|
/** Сальдо со знаком (Дт − Кт) текстом: «1 200,00 Дт», «300,00 Кт». */
|
|
@@ -304,32 +372,27 @@ export default {
|
|
|
304
372
|
}
|
|
305
373
|
.analysis-side {
|
|
306
374
|
@apply --vertical;
|
|
307
|
-
width:
|
|
375
|
+
width: 245px;
|
|
308
376
|
flex-shrink: 0;
|
|
309
|
-
gap:
|
|
310
|
-
padding:
|
|
377
|
+
gap: 0;
|
|
378
|
+
padding: 0;
|
|
311
379
|
background: light-dark(#f3f2ea, #22232a);
|
|
312
380
|
border-right: 1px solid light-dark(#9a9990, #4a4c56);
|
|
313
|
-
overflow
|
|
381
|
+
overflow: hidden;
|
|
314
382
|
font-size: 12px;
|
|
315
383
|
}
|
|
316
|
-
.
|
|
317
|
-
@apply --vertical;
|
|
318
|
-
gap: 4px;
|
|
319
|
-
padding: 6px;
|
|
320
|
-
background: light-dark(#fafaf7, #292a32);
|
|
321
|
-
border: 1px solid light-dark(#c8c7be, #3e4049);
|
|
322
|
-
border-radius: 3px;
|
|
323
|
-
}
|
|
324
|
-
.slot-head {
|
|
384
|
+
.analysis-side-top {
|
|
325
385
|
@apply --horizontal;
|
|
326
386
|
align-items: center;
|
|
327
387
|
gap: 4px;
|
|
328
|
-
|
|
388
|
+
padding: 4px 6px;
|
|
389
|
+
border-bottom: 1px solid light-dark(#b8b7ae, #3e4049);
|
|
390
|
+
background: light-dark(#e8e7dc, #26272f);
|
|
391
|
+
flex-shrink: 0;
|
|
329
392
|
}
|
|
330
393
|
.slot-move-btn {
|
|
331
|
-
padding:
|
|
332
|
-
font-size:
|
|
394
|
+
padding: 2px 7px;
|
|
395
|
+
font-size: 12px;
|
|
333
396
|
line-height: 1.2;
|
|
334
397
|
border: 1px solid light-dark(#b8b7ae, #4a4c56);
|
|
335
398
|
border-radius: 2px;
|
|
@@ -337,40 +400,90 @@ export default {
|
|
|
337
400
|
color: inherit;
|
|
338
401
|
cursor: pointer;
|
|
339
402
|
}
|
|
340
|
-
.slot-
|
|
341
|
-
|
|
342
|
-
box-sizing: border-box;
|
|
343
|
-
padding: 3px 6px;
|
|
344
|
-
border: 1px solid light-dark(#b8b7ae, #3e4049);
|
|
345
|
-
border-radius: 3px;
|
|
346
|
-
background: light-dark(#fff, #1b1c20);
|
|
347
|
-
color: inherit;
|
|
348
|
-
font-size: 11.5px;
|
|
403
|
+
.slot-move-btn:hover {
|
|
404
|
+
background: light-dark(#eef2fa, #3d404c);
|
|
349
405
|
}
|
|
350
|
-
.
|
|
406
|
+
.features-grid {
|
|
351
407
|
@apply --vertical;
|
|
352
408
|
flex: 1;
|
|
353
|
-
min-width: 0;
|
|
354
409
|
min-height: 0;
|
|
410
|
+
overflow-y: auto;
|
|
411
|
+
background: light-dark(#ffffff, #1b1c20);
|
|
412
|
+
border-bottom: 1px solid light-dark(#b8b7ae, #3e4049);
|
|
413
|
+
}
|
|
414
|
+
.feat-head, .feat-row {
|
|
415
|
+
display: grid !important;
|
|
416
|
+
grid-template-columns: minmax(105px, 1.05fr) minmax(95px, 1fr);
|
|
417
|
+
align-items: stretch;
|
|
418
|
+
}
|
|
419
|
+
.feat-head {
|
|
420
|
+
background: light-dark(#e3e1d3, #2a2b32);
|
|
421
|
+
color: light-dark(#1f1f1f, #e2e8f0);
|
|
422
|
+
border-bottom: 1px solid light-dark(#9a9990, #4a4c56);
|
|
423
|
+
font-weight: 600;
|
|
424
|
+
font-size: 11.5px;
|
|
425
|
+
flex-shrink: 0;
|
|
426
|
+
}
|
|
427
|
+
.feat-row {
|
|
428
|
+
min-height: 24px;
|
|
429
|
+
border-bottom: 1px solid light-dark(#ecece8, #2c2d34);
|
|
430
|
+
cursor: pointer;
|
|
431
|
+
flex-shrink: 0;
|
|
432
|
+
}
|
|
433
|
+
.feat-row.selected {
|
|
434
|
+
background: light-dark(#dbe4f8, rgba(104, 130, 214, 0.28));
|
|
435
|
+
}
|
|
436
|
+
.feat-cell {
|
|
437
|
+
min-width: 0;
|
|
438
|
+
padding: 2px 5px;
|
|
439
|
+
display: flex;
|
|
440
|
+
align-items: center;
|
|
441
|
+
gap: 4px;
|
|
355
442
|
overflow: hidden;
|
|
443
|
+
text-overflow: ellipsis;
|
|
444
|
+
white-space: nowrap;
|
|
445
|
+
border-right: 1px solid light-dark(#e2e2dc, #2f3038);
|
|
446
|
+
box-sizing: border-box;
|
|
356
447
|
}
|
|
357
|
-
.
|
|
358
|
-
|
|
448
|
+
.feat-cell:last-child {
|
|
449
|
+
border-right: none;
|
|
450
|
+
}
|
|
451
|
+
.feat-select {
|
|
452
|
+
width: 100%;
|
|
453
|
+
min-width: 0;
|
|
454
|
+
padding: 1px 3px;
|
|
455
|
+
font-size: 11.5px;
|
|
456
|
+
border: 1px solid transparent;
|
|
457
|
+
border-radius: 2px;
|
|
458
|
+
background: transparent;
|
|
459
|
+
color: light-dark(#1d4ed8, #93c5fd);
|
|
460
|
+
cursor: pointer;
|
|
461
|
+
outline: none;
|
|
462
|
+
}
|
|
463
|
+
.feat-select:focus, .feat-select:hover {
|
|
464
|
+
border-color: light-dark(#b8b7ae, #4a4c56);
|
|
465
|
+
background: light-dark(#fff, #24252c);
|
|
466
|
+
}
|
|
467
|
+
.analysis-side-bottom {
|
|
468
|
+
@apply --vertical;
|
|
469
|
+
gap: 3px;
|
|
470
|
+
padding: 6px 8px;
|
|
359
471
|
flex-shrink: 0;
|
|
360
|
-
|
|
472
|
+
background: light-dark(#f3f2ea, #22232a);
|
|
473
|
+
}
|
|
474
|
+
.analysis-side-bottom label {
|
|
475
|
+
display: flex;
|
|
361
476
|
align-items: center;
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
477
|
+
gap: 5px;
|
|
478
|
+
cursor: pointer;
|
|
479
|
+
user-select: none;
|
|
365
480
|
}
|
|
366
|
-
.analysis-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
background: light-dark(#fff, #1b1c20);
|
|
373
|
-
color: inherit;
|
|
481
|
+
.analysis-main {
|
|
482
|
+
@apply --vertical;
|
|
483
|
+
flex: 1;
|
|
484
|
+
min-width: 0;
|
|
485
|
+
min-height: 0;
|
|
486
|
+
overflow: hidden;
|
|
374
487
|
}
|
|
375
488
|
.analysis-table {
|
|
376
489
|
@apply --vertical;
|
|
@@ -378,11 +491,37 @@ export default {
|
|
|
378
491
|
min-height: 0;
|
|
379
492
|
overflow: hidden;
|
|
380
493
|
}
|
|
381
|
-
.analysis-table div.head-top, .analysis-table div.head, .analysis-table div.row {
|
|
494
|
+
.analysis-table div.head-top, .analysis-table div.head, .analysis-table div.filter-row, .analysis-table div.row {
|
|
382
495
|
display: grid !important;
|
|
383
496
|
grid-template-columns: minmax(210px, 2.5fr) repeat(6, minmax(90px, 1fr));
|
|
384
497
|
align-items: stretch;
|
|
385
498
|
}
|
|
499
|
+
.filter-row {
|
|
500
|
+
background: light-dark(#ffffff, #1f2026);
|
|
501
|
+
border-top: 1px solid light-dark(#b8b7ae, #3e4049);
|
|
502
|
+
min-height: 23px;
|
|
503
|
+
}
|
|
504
|
+
.filter-row .cell {
|
|
505
|
+
padding: 1px 5px;
|
|
506
|
+
gap: 4px;
|
|
507
|
+
}
|
|
508
|
+
.filter-mark {
|
|
509
|
+
font-size: 10.5px;
|
|
510
|
+
font-weight: 700;
|
|
511
|
+
color: light-dark(#555, #94a3b8);
|
|
512
|
+
flex-shrink: 0;
|
|
513
|
+
user-select: none;
|
|
514
|
+
}
|
|
515
|
+
.inline-filter-input {
|
|
516
|
+
flex: 1;
|
|
517
|
+
min-width: 0;
|
|
518
|
+
border: none;
|
|
519
|
+
outline: none;
|
|
520
|
+
background: transparent;
|
|
521
|
+
color: inherit;
|
|
522
|
+
font-size: 12px;
|
|
523
|
+
padding: 1px 2px;
|
|
524
|
+
}
|
|
386
525
|
.analysis-table .grid-body div.row .cell.acc {
|
|
387
526
|
background: light-dark(#f6f6f4, #222328);
|
|
388
527
|
}
|
|
@@ -394,31 +533,12 @@ export default {
|
|
|
394
533
|
}
|
|
395
534
|
|
|
396
535
|
/* Экран 3: Обороты счёта (mode === 'turnovers') */
|
|
397
|
-
.group-panel {
|
|
398
|
-
@apply --vertical;
|
|
399
|
-
width: 124px;
|
|
400
|
-
flex-shrink: 0;
|
|
401
|
-
gap: 4px;
|
|
402
|
-
padding: 8px;
|
|
403
|
-
background: light-dark(#f3f2ea, #22232a);
|
|
404
|
-
border-right: 1px solid light-dark(#9a9990, #4a4c56);
|
|
405
|
-
overflow-y: auto;
|
|
406
|
-
font-size: 12.5px;
|
|
407
|
-
}
|
|
408
|
-
.group-panel label {
|
|
409
|
-
display: flex;
|
|
410
|
-
align-items: center;
|
|
411
|
-
gap: 5px;
|
|
412
|
-
padding: 1px 0;
|
|
413
|
-
cursor: pointer;
|
|
414
|
-
user-select: none;
|
|
415
|
-
}
|
|
416
536
|
.group-title {
|
|
417
537
|
font-weight: 700;
|
|
418
538
|
font-size: 11.5px;
|
|
419
539
|
letter-spacing: 0.02em;
|
|
420
|
-
color: light-dark(#
|
|
421
|
-
margin-bottom:
|
|
540
|
+
color: light-dark(#555, #cbd5e1);
|
|
541
|
+
margin-bottom: 2px;
|
|
422
542
|
}
|
|
423
543
|
.periods-panel {
|
|
424
544
|
@apply --vertical;
|
|
@@ -445,9 +565,31 @@ export default {
|
|
|
445
565
|
}
|
|
446
566
|
.docs-panel div.head, .docs-panel div.row {
|
|
447
567
|
display: grid !important;
|
|
448
|
-
grid-template-columns: 82px minmax(115px, 1.45fr) minmax(115px, 1.35fr) minmax(86px, 1fr) minmax(86px, 1fr);
|
|
568
|
+
grid-template-columns: 82px minmax(115px, 1.45fr) minmax(115px, 1.35fr) minmax(86px, 1fr) minmax(86px, 1fr) 30px;
|
|
449
569
|
align-items: stretch;
|
|
450
570
|
}
|
|
571
|
+
.cell.entry-col {
|
|
572
|
+
justify-content: center;
|
|
573
|
+
padding: 0 2px;
|
|
574
|
+
}
|
|
575
|
+
.entry-icon-btn {
|
|
576
|
+
display: inline-flex;
|
|
577
|
+
align-items: center;
|
|
578
|
+
justify-content: center;
|
|
579
|
+
width: 22px;
|
|
580
|
+
height: 20px;
|
|
581
|
+
padding: 0;
|
|
582
|
+
border: 1px solid transparent;
|
|
583
|
+
border-radius: 3px;
|
|
584
|
+
background: transparent;
|
|
585
|
+
color: light-dark(#444, #cbd5e1);
|
|
586
|
+
cursor: pointer;
|
|
587
|
+
}
|
|
588
|
+
.entry-icon-btn:hover {
|
|
589
|
+
border-color: light-dark(#b8b7ae, #4a4c56);
|
|
590
|
+
background: light-dark(#eef2fa, #2d3039);
|
|
591
|
+
color: light-dark(#1d4ed8, #93c5fd);
|
|
592
|
+
}
|
|
451
593
|
|
|
452
594
|
/* Экран 4: Сборная карточка проводки */
|
|
453
595
|
.entry-card {
|
|
@@ -503,8 +645,12 @@ export default {
|
|
|
503
645
|
|
|
504
646
|
<div class="error" ~if="error">{{error}}</div>
|
|
505
647
|
|
|
506
|
-
<!-- Компактная полоса отбора по аналитикам и подразделению в Оборотах и Проводках -->
|
|
507
|
-
<div class="filter-strip" ~if="(slots?.length || units.length > 1)
|
|
648
|
+
<!-- Компактная полоса группировки, отбора по аналитикам и подразделению в Оборотах и Проводках -->
|
|
649
|
+
<div class="filter-strip" ~if="mode !== 'analysis' && (mode === 'turnovers' || slots?.length || units.length > 1)">
|
|
650
|
+
<span class="filter-strip-label" ~if="mode === 'turnovers'">Группировать:</span>
|
|
651
|
+
<select ~if="mode === 'turnovers'" title="Группировать" @change="setGroupBy($this.value)">
|
|
652
|
+
<option ~for="turnoverGroupOptions" :value="$for.item.id" :selected="$for.item.id === groupBy">{{$for.item.label}}</option>
|
|
653
|
+
</select>
|
|
508
654
|
<span class="filter-strip-label" ~if="slots?.length">Аналитики:</span>
|
|
509
655
|
<div class="filter-slot-item" ~for="slots">
|
|
510
656
|
<oda-link-input dense :meta="$for.item" ::value="filters[$for.item.id]" @value-changed="onFilterChanged()"></oda-link-input>
|
|
@@ -563,32 +709,42 @@ export default {
|
|
|
563
709
|
</div>
|
|
564
710
|
</div>
|
|
565
711
|
|
|
566
|
-
<!-- Экран 2 ODANT: Анализ счёта в разрезе аналитик -->
|
|
712
|
+
<!-- Экран 2 ODANT: Анализ счёта в разрезе аналитик (по полям прилинкованных справочников) -->
|
|
567
713
|
<div class="analysis-layout" ~if="mode === 'analysis'">
|
|
568
714
|
<div class="analysis-side">
|
|
569
|
-
<div class="
|
|
570
|
-
|
|
571
|
-
<
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
715
|
+
<div class="analysis-side-top">
|
|
716
|
+
<button type="button" class="slot-move-btn" title="Поднять признак выше" @tap="moveFeature(-1)">↑</button>
|
|
717
|
+
<button type="button" class="slot-move-btn" title="Опустить признак ниже" @tap="moveFeature(1)">↓</button>
|
|
718
|
+
<div style="flex:1;"></div>
|
|
719
|
+
<button type="button" class="btn-action" @tap="reload()">Сформировать</button>
|
|
720
|
+
</div>
|
|
721
|
+
<div class="features-grid">
|
|
722
|
+
<div class="feat-head">
|
|
723
|
+
<div class="feat-cell">Аналитика</div>
|
|
724
|
+
<div class="feat-cell">Значение</div>
|
|
725
|
+
</div>
|
|
726
|
+
<div class="feat-row" ~for="analyticFeatures"
|
|
727
|
+
~class="{selected: selectedFeatureId === $for.item.id}"
|
|
728
|
+
@tap="selectedFeatureId = $for.item.id">
|
|
729
|
+
<div class="feat-cell" :title="featureLabel($for.item)">
|
|
730
|
+
<input type="checkbox" :checked="isSlotActive($for.item.spec)" @change.stop="toggleSlot($for.item.spec, $this.checked)">
|
|
731
|
+
<span style="overflow:hidden; text-overflow:ellipsis;">{{featureLabel($for.item)}}</span>
|
|
732
|
+
</div>
|
|
733
|
+
<div class="feat-cell">
|
|
734
|
+
<select class="feat-select" :title="featureValueLabel($for.item)" @change="setFeatureValue($for.item, $this.value)">
|
|
735
|
+
<option value="" :selected="!featureValueOf($for.item)">Все</option>
|
|
736
|
+
<option ~for="$for.item.options" :value="$for.$for.item.value" :selected="$for.$for.item.value === featureValueOf($for.item)">{{$for.$for.item.label}}</option>
|
|
737
|
+
</select>
|
|
738
|
+
</div>
|
|
576
739
|
</div>
|
|
577
|
-
<oda-link-input dense :meta="$for.item" ::value="filters[$for.item.id]" @value-changed="onFilterChanged()"></oda-link-input>
|
|
578
|
-
<input class="slot-sub" :value="subFilters[$for.item.id] || ''"
|
|
579
|
-
placeholder="Субаналитика (поле:знач)"
|
|
580
|
-
@change="setSubFilter($for.item.id, $this.value)">
|
|
581
740
|
</div>
|
|
582
|
-
<div class="
|
|
583
|
-
|
|
584
|
-
|
|
741
|
+
<div class="analysis-side-bottom">
|
|
742
|
+
<div class="group-title">ЗНАЧЕНИЯ</div>
|
|
743
|
+
<label><input type="radio" name="valMode" value="amount" :checked="valueMode === 'amount'" @change="setValueMode('amount')"> Сумма</label>
|
|
744
|
+
<label><input type="radio" name="valMode" value="qty" :checked="valueMode === 'qty'" @change="setValueMode('qty')"> Количество</label>
|
|
745
|
+
</div>
|
|
585
746
|
</div>
|
|
586
747
|
<div class="analysis-main">
|
|
587
|
-
<div class="analysis-toolbar">
|
|
588
|
-
<input class="analysis-search" :value="analysisQuery" placeholder="Быстрый фильтр по аналитике…" @input="analysisQuery = $this.value">
|
|
589
|
-
<div style="flex:1;"></div>
|
|
590
|
-
<button type="button" class="btn-action" @tap="openTurnoversFromAnalysis(selectedAnalysisRow)">Обороты по аналитике</button>
|
|
591
|
-
</div>
|
|
592
748
|
<div class="analysis-table">
|
|
593
749
|
<div class="head-wrap">
|
|
594
750
|
<div class="head-top">
|
|
@@ -606,6 +762,18 @@ export default {
|
|
|
606
762
|
<div class="cell num">Дебет</div>
|
|
607
763
|
<div class="cell num">Кредит</div>
|
|
608
764
|
</div>
|
|
765
|
+
<div class="filter-row">
|
|
766
|
+
<div class="cell acc">
|
|
767
|
+
<span class="filter-mark">a%c</span>
|
|
768
|
+
<input class="inline-filter-input" :value="analysisQuery" placeholder="Фильтр…" @input="analysisQuery = $this.value">
|
|
769
|
+
</div>
|
|
770
|
+
<div class="cell"><span class="filter-mark">=</span></div>
|
|
771
|
+
<div class="cell group-sep"><span class="filter-mark">=</span></div>
|
|
772
|
+
<div class="cell"><span class="filter-mark">=</span></div>
|
|
773
|
+
<div class="cell group-sep"><span class="filter-mark">=</span></div>
|
|
774
|
+
<div class="cell"><span class="filter-mark">=</span></div>
|
|
775
|
+
<div class="cell"><span class="filter-mark">=</span></div>
|
|
776
|
+
</div>
|
|
609
777
|
</div>
|
|
610
778
|
<div class="grid-body">
|
|
611
779
|
<div class="row" ~for="analysisRows"
|
|
@@ -636,13 +804,6 @@ export default {
|
|
|
636
804
|
|
|
637
805
|
<!-- Экран 3 ODANT: Двухпанельные Обороты счёта -->
|
|
638
806
|
<div class="turnovers-layout" ~if="mode === 'turnovers'">
|
|
639
|
-
<div class="group-panel">
|
|
640
|
-
<div class="group-title">ГРУППИРОВАТЬ</div>
|
|
641
|
-
<label ~for="turnoverGroupOptions">
|
|
642
|
-
<input type="radio" name="turnoverGroup" :value="$for.item.id" :checked="groupBy === $for.item.id" @change="setGroupBy($for.item.id)">
|
|
643
|
-
<span>{{$for.item.label}}</span>
|
|
644
|
-
</label>
|
|
645
|
-
</div>
|
|
646
807
|
<div class="periods-panel">
|
|
647
808
|
<div class="head-wrap">
|
|
648
809
|
<div class="head">
|
|
@@ -687,6 +848,7 @@ export default {
|
|
|
687
848
|
<div class="cell">Корр. счёт</div>
|
|
688
849
|
<div class="cell num">Дебет</div>
|
|
689
850
|
<div class="cell num">Кредит</div>
|
|
851
|
+
<div class="cell entry-col" title="Проводка"></div>
|
|
690
852
|
</div>
|
|
691
853
|
</div>
|
|
692
854
|
<div class="grid-body">
|
|
@@ -696,6 +858,11 @@ export default {
|
|
|
696
858
|
<div class="cell link" title="Открыть корреспондирующий счёт" @tap="openCorr($for.item)">{{$for.item.corrDir}}</div>
|
|
697
859
|
<div class="cell num debit-col">{{$for.item.debit}}</div>
|
|
698
860
|
<div class="cell num credit-col">{{$for.item.credit}}</div>
|
|
861
|
+
<div class="cell entry-col">
|
|
862
|
+
<button type="button" class="entry-icon-btn" title="Открыть проводку" @tap.stop="openEntry($for.item.entry)">
|
|
863
|
+
<oda-icon icon="icons:list" icon-size="16"></oda-icon>
|
|
864
|
+
</button>
|
|
865
|
+
</div>
|
|
699
866
|
</div>
|
|
700
867
|
</div>
|
|
701
868
|
<div class="row total">
|
|
@@ -704,6 +871,7 @@ export default {
|
|
|
704
871
|
<div class="cell"></div>
|
|
705
872
|
<div class="cell num">{{turnoverDocsTotals.debit}}</div>
|
|
706
873
|
<div class="cell num">{{turnoverDocsTotals.credit}}</div>
|
|
874
|
+
<div class="cell entry-col"></div>
|
|
707
875
|
</div>
|
|
708
876
|
</div>
|
|
709
877
|
</div>
|
|
@@ -753,8 +921,13 @@ export default {
|
|
|
753
921
|
turnoverGroupOptions: TURNOVER_GROUPS,
|
|
754
922
|
selectedGroupKey: '',
|
|
755
923
|
groups: [],
|
|
756
|
-
/** Выбранные
|
|
924
|
+
/** Выбранные слоты/признаки для разворота в Анализе счёта */
|
|
757
925
|
activeSlots: [],
|
|
926
|
+
/** Аналитические признаки по полям прилинкованных справочников */
|
|
927
|
+
analyticFeatures: [],
|
|
928
|
+
selectedFeatureId: '',
|
|
929
|
+
/** Значения фильтра по полям прилинкованных справочников: { 'slotId.fieldId': val } */
|
|
930
|
+
featureValues: {},
|
|
758
931
|
/** Текстовые фильтры субаналитик: { слот: 'поле:значение' } */
|
|
759
932
|
subFilters: {},
|
|
760
933
|
analysisQuery: '',
|
|
@@ -930,6 +1103,61 @@ export default {
|
|
|
930
1103
|
get analysisTotals() {
|
|
931
1104
|
return this.analysisCells(this.sheetTotal);
|
|
932
1105
|
},
|
|
1106
|
+
featureLabel(feat) {
|
|
1107
|
+
if (!feat)
|
|
1108
|
+
return '';
|
|
1109
|
+
if ((this.slots || []).length > 1 && feat.slotLabel)
|
|
1110
|
+
return `${feat.slotLabel}: ${feat.label || feat.id}`;
|
|
1111
|
+
return feat.label || feat.id;
|
|
1112
|
+
},
|
|
1113
|
+
featureValueOf(feat) {
|
|
1114
|
+
if (!feat)
|
|
1115
|
+
return '';
|
|
1116
|
+
if (feat.isObject)
|
|
1117
|
+
return String(this.filters?.[feat.slotId] || '');
|
|
1118
|
+
return String(this.featureValues?.[feat.id] || '');
|
|
1119
|
+
},
|
|
1120
|
+
featureValueLabel(feat) {
|
|
1121
|
+
const val = this.featureValueOf(feat);
|
|
1122
|
+
if (!val)
|
|
1123
|
+
return 'Все';
|
|
1124
|
+
const opt = (feat?.options || []).find(o => o.value === val);
|
|
1125
|
+
return opt?.label || val;
|
|
1126
|
+
},
|
|
1127
|
+
async setFeatureValue(feat, value) {
|
|
1128
|
+
if (!feat)
|
|
1129
|
+
return;
|
|
1130
|
+
const val = String(value || '');
|
|
1131
|
+
if (feat.isObject) {
|
|
1132
|
+
this.filters = { ...(this.filters || {}), [feat.slotId]: val };
|
|
1133
|
+
await this.onFilterChanged();
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
this.featureValues = { ...(this.featureValues || {}), [feat.id]: val };
|
|
1137
|
+
if (val && !(this.activeSlots || []).includes(feat.spec) && !(this.activeSlots || []).includes(feat.slotId)) {
|
|
1138
|
+
this.activeSlots = [...(this.activeSlots || []), feat.spec];
|
|
1139
|
+
}
|
|
1140
|
+
await this.reload();
|
|
1141
|
+
},
|
|
1142
|
+
async moveFeature(delta) {
|
|
1143
|
+
const list = [...(this.analyticFeatures || [])];
|
|
1144
|
+
const idx = list.findIndex(f => f.id === this.selectedFeatureId);
|
|
1145
|
+
if (idx < 0)
|
|
1146
|
+
return;
|
|
1147
|
+
const target = idx + delta;
|
|
1148
|
+
if (target < 0 || target >= list.length)
|
|
1149
|
+
return;
|
|
1150
|
+
const [moved] = list.splice(idx, 1);
|
|
1151
|
+
list.splice(target, 0, moved);
|
|
1152
|
+
this.analyticFeatures = list;
|
|
1153
|
+
const order = list.map(f => f.spec);
|
|
1154
|
+
this.activeSlots = (this.activeSlots || []).slice().sort((a, b) => {
|
|
1155
|
+
const ia = order.indexOf(a), ib = order.indexOf(b);
|
|
1156
|
+
return (ia < 0 ? 999 : ia) - (ib < 0 ? 999 : ib);
|
|
1157
|
+
});
|
|
1158
|
+
if (this.mode === 'analysis')
|
|
1159
|
+
await this.reload();
|
|
1160
|
+
},
|
|
933
1161
|
isSlotActive(slotId) {
|
|
934
1162
|
return (this.activeSlots || []).includes(slotId);
|
|
935
1163
|
},
|
|
@@ -939,7 +1167,13 @@ export default {
|
|
|
939
1167
|
cur.add(slotId);
|
|
940
1168
|
else
|
|
941
1169
|
cur.delete(slotId);
|
|
942
|
-
this.
|
|
1170
|
+
const order = (this.analyticFeatures || []).map(f => f.spec);
|
|
1171
|
+
this.activeSlots = [...cur].sort((a, b) => {
|
|
1172
|
+
const ia = order.indexOf(a), ib = order.indexOf(b);
|
|
1173
|
+
if (ia < 0 && ib < 0)
|
|
1174
|
+
return 0;
|
|
1175
|
+
return (ia < 0 ? 999 : ia) - (ib < 0 ? 999 : ib);
|
|
1176
|
+
});
|
|
943
1177
|
if (this.mode === 'analysis')
|
|
944
1178
|
await this.reload();
|
|
945
1179
|
},
|
|
@@ -953,6 +1187,9 @@ export default {
|
|
|
953
1187
|
this.slots = list;
|
|
954
1188
|
const order = list.map(s => s.id);
|
|
955
1189
|
this.activeSlots = (this.activeSlots || []).slice().sort((a, b) => order.indexOf(a) - order.indexOf(b));
|
|
1190
|
+
if (this.analyticFeatures?.length) {
|
|
1191
|
+
this.analyticFeatures = [...this.analyticFeatures].sort((a, b) => order.indexOf(a.slotId) - order.indexOf(b.slotId));
|
|
1192
|
+
}
|
|
956
1193
|
if (this.mode === 'analysis')
|
|
957
1194
|
await this.reload();
|
|
958
1195
|
},
|
|
@@ -1090,14 +1327,23 @@ export default {
|
|
|
1090
1327
|
if (!hasNavPeriod && (this.from || this.to))
|
|
1091
1328
|
return;
|
|
1092
1329
|
const saved = _readSavedPeriod();
|
|
1093
|
-
const
|
|
1330
|
+
const pk = typeof this.packetsKey === 'string' ? this.packetsKey : '';
|
|
1331
|
+
const src = hasNavPeriod ? nav : (saved || (pk && pk !== '*' ? { packetsKey: pk } : null));
|
|
1094
1332
|
if (!src)
|
|
1095
1333
|
return;
|
|
1096
1334
|
this._initializingPackets = true;
|
|
1097
1335
|
try {
|
|
1098
|
-
const
|
|
1099
|
-
|
|
1100
|
-
|
|
1336
|
+
const rawKey = typeof src.packetsKey === 'string' && src.packetsKey ? src.packetsKey : (pk || '*');
|
|
1337
|
+
let from = String(src.from || '');
|
|
1338
|
+
let to = String(src.to || '');
|
|
1339
|
+
if ((!from && !to && rawKey !== '*') || rawKey === 'now' || rawKey.startsWith('now:')) {
|
|
1340
|
+
const derived = _boundsFromPacketsKey(rawKey, 'month');
|
|
1341
|
+
if (derived.from && derived.to) {
|
|
1342
|
+
from = derived.from;
|
|
1343
|
+
to = derived.to;
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
const key = _derivePacketsKey(from, to, rawKey);
|
|
1101
1347
|
this.from = from;
|
|
1102
1348
|
this.to = to;
|
|
1103
1349
|
this._packetFiltered = !!(from || to);
|
|
@@ -1170,18 +1416,45 @@ export default {
|
|
|
1170
1416
|
this.units = [];
|
|
1171
1417
|
}
|
|
1172
1418
|
},
|
|
1173
|
-
/** Слоты аналитик счёта из схемы ядра (с учётом наследования)
|
|
1419
|
+
/** Слоты аналитик счёта из схемы ядра (с учётом наследования) и поля прилинкованных справочников. */
|
|
1174
1420
|
async loadSlots() {
|
|
1175
1421
|
try {
|
|
1176
|
-
const
|
|
1422
|
+
const res = await this.$item?.fetch('db_schema');
|
|
1423
|
+
const s = res?.schema;
|
|
1177
1424
|
const fields = Array.isArray(s?.fields) ? s.fields : [];
|
|
1178
1425
|
this.slots = fields.filter(f => f?.analytic && f.type === 'link')
|
|
1179
1426
|
.map(f => ({ id: f.id, label: f.label || f.id, type: 'Link', catalog: f.targetPath || f.target, placeholder: f.label || f.id }));
|
|
1180
1427
|
this.activeSlots = this.slots.map(f => f.id);
|
|
1428
|
+
const serverSlots = Array.isArray(res?.slots) ? res.slots : [];
|
|
1429
|
+
const features = [];
|
|
1430
|
+
for (const slot of this.slots) {
|
|
1431
|
+
const srv = serverSlots.find(x => x.id === slot.id);
|
|
1432
|
+
if (Array.isArray(srv?.fields) && srv.fields.length) {
|
|
1433
|
+
features.push(...srv.fields);
|
|
1434
|
+
}
|
|
1435
|
+
else {
|
|
1436
|
+
features.push({
|
|
1437
|
+
id: slot.id,
|
|
1438
|
+
spec: slot.id,
|
|
1439
|
+
slotId: slot.id,
|
|
1440
|
+
fieldId: 'name',
|
|
1441
|
+
label: 'Наименование',
|
|
1442
|
+
slotLabel: slot.label,
|
|
1443
|
+
isObject: true,
|
|
1444
|
+
type: 'link',
|
|
1445
|
+
catalog: slot.catalog,
|
|
1446
|
+
options: [],
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
this.analyticFeatures = features;
|
|
1451
|
+
this.selectedFeatureId = features[0]?.id || '';
|
|
1181
1452
|
}
|
|
1182
1453
|
catch (e) {
|
|
1183
1454
|
this.slots = [];
|
|
1184
1455
|
this.activeSlots = [];
|
|
1456
|
+
this.analyticFeatures = [];
|
|
1457
|
+
this.selectedFeatureId = '';
|
|
1185
1458
|
this.error = String(e?.message || e);
|
|
1186
1459
|
}
|
|
1187
1460
|
},
|
|
@@ -1200,7 +1473,17 @@ export default {
|
|
|
1200
1473
|
for (const [slotId, raw] of Object.entries(this.subFilters || {})) {
|
|
1201
1474
|
const parsed = parseSubFilterInput(raw);
|
|
1202
1475
|
if (parsed)
|
|
1203
|
-
out[slotId] = parsed;
|
|
1476
|
+
out[slotId] = { ...(out[slotId] || {}), ...parsed };
|
|
1477
|
+
}
|
|
1478
|
+
for (const feat of this.analyticFeatures || []) {
|
|
1479
|
+
if (feat.isObject)
|
|
1480
|
+
continue;
|
|
1481
|
+
const val = this.featureValues?.[feat.id];
|
|
1482
|
+
if (val !== undefined && val !== null && val !== '') {
|
|
1483
|
+
if (!out[feat.slotId])
|
|
1484
|
+
out[feat.slotId] = {};
|
|
1485
|
+
out[feat.slotId][feat.fieldId] = feat.type === 'boolean' ? (val === '1' || val === 'true' ? 1 : 0) : val;
|
|
1486
|
+
}
|
|
1204
1487
|
}
|
|
1205
1488
|
return out;
|
|
1206
1489
|
},
|
|
@@ -1232,7 +1515,9 @@ export default {
|
|
|
1232
1515
|
this.packetsStart = r.start;
|
|
1233
1516
|
this.packetsEnd = r.end;
|
|
1234
1517
|
this.packetDays = Array.isArray(r.days) && r.days.length ? r.days : null;
|
|
1235
|
-
|
|
1518
|
+
const keyStr = typeof this.packetsKey === 'string' ? this.packetsKey : '';
|
|
1519
|
+
const isNowKey = keyStr === 'now' || keyStr.startsWith('now:');
|
|
1520
|
+
if (this._packetFiltered && !isNowKey && this.to && this.to < r.start) {
|
|
1236
1521
|
this._initializingPackets = true;
|
|
1237
1522
|
try {
|
|
1238
1523
|
this._packetFiltered = false;
|