]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Фильтры и даты
authorVladimir Fomichev <vladimir.fomichev@erp-flowers.ru>
Tue, 19 Aug 2025 11:34:09 +0000 (14:34 +0300)
committerVladimir Fomichev <vladimir.fomichev@erp-flowers.ru>
Tue, 19 Aug 2025 11:34:09 +0000 (14:34 +0300)
erp24/controllers/Products1cNomenclatureActualityController.php
erp24/views/products1c-nomenclature-actuality/index.php
erp24/web/js/products1cNomenclatureActuality/index.js

index 222cde25c80bcb2a37b56ee2db1be4173a208c09..641c335a3ee0c41e9d2ddde6b112635699d7b9e3 100644 (file)
@@ -118,13 +118,13 @@ class Products1cNomenclatureActualityController extends Controller
                 }
             }
 
-            if ($filter->onlyActive) {
+            if (!empty($filter->onlyActive)) {
                 $query->andWhere(['exists',
                     Products1cNomenclatureActuality::find()
                         ->where('guid = n.id')
                         ->select(new \yii\db\Expression('1'))
                 ]);
-            } elseif ($filter->onlyInactive) {
+            } elseif (!empty($filter->onlyInactive)) {
                 $query->andWhere(['not exists',
                     Products1cNomenclatureActuality::find()
                         ->where('guid = n.id')
@@ -133,23 +133,62 @@ class Products1cNomenclatureActualityController extends Controller
             }
 
             if ($filter->date_from || $filter->date_to) {
-                $query->with(['actualities' => function ($q) use ($filter) {
-                    if ($filter->date_from) {
-                        $df = (new \DateTime("{$filter->date_from}-01"))
-                            ->setTime(0, 0, 0)->format('Y-m-d H:i:s');
-                        $q->andWhere(['>=', 'date_to', $df]);
-                    }
-                    if ($filter->date_to) {
-                        $dt = (new \DateTime("{$filter->date_to}-01"))
-                            ->modify('last day of this month')->setTime(23, 59, 59)
-                            ->format('Y-m-d H:i:s');
-                        $q->andWhere(['<=', 'date_from', $dt]);
+                $hasDf = !empty($filter->date_from);
+                $hasDt = !empty($filter->date_to);
+
+                $df = null;
+                $dt = null;
+
+                if ($hasDf) {
+                    $df = (new \DateTime("{$filter->date_from}-01"))
+                        ->setTime(0, 0, 0)->format('Y-m-d H:i:s');
+                }
+                if ($hasDt) {
+                    $dt = (new \DateTime("{$filter->date_to}-01"))
+                        ->modify('last day of this month')->setTime(23, 59, 59)
+                        ->format('Y-m-d H:i:s');
+                }
+
+                $dateExists = Products1cNomenclatureActuality::find()
+                    ->alias('a')
+                    ->where('a.guid = n.id');
+
+                if ($hasDf && !$hasDt) {
+                    $dateExists->andWhere(['a.date_from' => $df]);
+                } elseif (!$hasDf && $hasDt) {
+                    $dateExists->andWhere(['a.date_to' => $dt]);
+                } else {
+                    $dateExists
+                        ->andWhere(['>=', 'a.date_from', $df])
+                        ->andWhere(['<=', 'a.date_to',   $dt]);
+                }
+
+                if (!empty($filter->onlyInactive)) {
+                    $query->andWhere(['not exists', $dateExists->select(new \yii\db\Expression('1'))]);
+                } else {
+                    $query->andWhere(['exists', $dateExists->select(new \yii\db\Expression('1'))]);
+                }
+
+                $query->with(['actualities' => function ($q) use ($hasDf, $hasDt, $df, $dt) {
+                    if ($hasDf && !$hasDt) {
+                        $q->andWhere(['date_from' => $df]);
+                    } elseif (!$hasDf && $hasDt) {
+                        $q->andWhere(['date_to' => $dt]);
+                    } else {
+                        $q->andWhere(['>=', 'date_from', $df])
+                            ->andWhere(['<=', 'date_to',   $dt]);
                     }
+                    $q->orderBy(['date_from' => SORT_ASC]);
                 }]);
             } else {
-                $query->with('actualities');
+                $query->with(['actualities' => function ($q) {
+                    $q->orderBy(['date_from' => SORT_ASC]);
+                }]);
             }
 
+
+
+
             $products = $query->orderBy(['n.name' => SORT_ASC])->all();
 
             $rows = [];
index a8be5ac6e619043d7487d000374b8feba8c1c205..fba62078045f186907aa49de1c6c2c6bb27698de 100644 (file)
@@ -26,7 +26,7 @@ function monthList()
 {
     $list = [];
     $start = new DateTime('2021-01');
-    $end = new DateTime('2025-12');
+    $end = new DateTime('2026-12');
     while ($start <= $end) {
         $key = $start->format('Y-m');
         $list[$key] = $start->format('Y‑m');
@@ -290,11 +290,13 @@ foreach ($months as $k => $v) {
                         Html::dropDownList("actuality[$i][from]", $from, $months, [
                             'class'=>'form-select from-month form-select-sm me-1',
                             'prompt'=>'от',
+                            'data-actuality' => $actuality ? 1 : 0,
                             'style' => 'width:auto;display:inline-block'
                         ]) .
                         Html::dropDownList("actuality[$i][to]", $to, $months, [
                             'class'=>'form-select to-month form-select-sm',
                             'prompt'=>'до',
+                            'data-actuality' => $actuality ? 1 : 0,
                             'style' => 'width:auto;display:inline-block'
                         ]),
                         ['class'=>'d-flex align-items-center']
index ccfb2f43eb030c62852dcc5a1dc9c756292e44e6..8d8ec7929b136ea315ddb24f0ce7fff51bf6df72 100644 (file)
@@ -4,6 +4,10 @@ document.addEventListener("DOMContentLoaded", () => {
         ([k, v]) => `<option value="${k}">${v}</option>`
     ).join('');
 
+    const baseMonths = getMonthsMap();
+    const monthsForNewRows = extendMonthsMap({ ...baseMonths }, 0);
+    const monthsForExisting = baseMonths;
+
     let actualIdx = $('#actuality-form table tbody tr').length || 0;
 
     $(document).on('click', '.add-actuality-row', function(){
@@ -26,7 +30,7 @@ document.addEventListener("DOMContentLoaded", () => {
                 <td>
                     <input type="hidden" name="actuality[${actualIdx}][guid]" value="${guid}">
                     <div class="d-flex align-items-center">
-                        <select name="actuality[${actualIdx}][from]" class="form-select from-month form-select-sm me-1" style="width:auto;display:inline-block">
+                        <select data-actuality="0" name="actuality[${actualIdx}][from]" class="form-select from-month form-select-sm me-1" style="width:auto;display:inline-block">
                             <option value="">от</option>${monthOptions}
                         </select>
                         <select name="actuality[${actualIdx}][to]" class="form-select to-month form-select-sm" style="width:auto;display:inline-block">
@@ -44,22 +48,32 @@ document.addEventListener("DOMContentLoaded", () => {
             </tr>
         `;
 
+        let $inserted;
         if ($lastRow.length) {
             $lastRow.after(newRow);
+            $inserted = $lastRow.next();
         } else {
             table.find('tbody').append(newRow);
+            $inserted = table.find('tbody tr').last();
         }
+
+        applyFromMonthLimits($inserted.find('.from-month'), monthsForNewRows);
+        syncToWithFrom($inserted.find('.from-month'));
+    });
+
+    $('.from-month').each(function () {
+        const hasActuality = $(this).data('actuality') == 1;
+        if (hasActuality) return;
+        applyFromMonthLimits($(this), monthsForExisting);
+    });
+    $('.to-month').each(function(){
+        const hasActuality = $(this).data('actuality') == 1;
+        if (hasActuality) return;
+        applyFromMonthLimits($(this), monthsForExisting);
     });
 
     $(document).on('change', '.from-month', function(){
-        var from = $(this).val(),
-            to   = $(this).closest('td').find('.to-month');
-        to.find('option').each(function(){
-            $(this).toggle($(this).val() >= from || $(this).val() === "");
-        });
-        if (to.val() < from) {
-            to.val(from);
-        }
+        syncToWithFrom($(this));
     });
 
     $('#filter-date-from').on('change', function(){
@@ -153,4 +167,89 @@ document.addEventListener("DOMContentLoaded", () => {
         checkIntervalsForGuid(guid);
     });
 
+    function ymParse(ym) {
+        const [y, m] = ym.split('-').map(Number);
+        return { y, m };
+    }
+    function ymFormat(y, m) {
+        return `${y}-${String(m).padStart(2, '0')}`;
+    }
+    function ymAdd(ym, deltaMonths) {
+        const { y, m } = ymParse(ym);
+        const d = new Date(y, m - 1 + deltaMonths, 1);
+        return ymFormat(d.getFullYear(), d.getMonth() + 1);
+    }
+    function getMinStartMonthByRule(today = new Date()) {
+        const y = today.getFullYear();
+        const m = today.getMonth(); // 0..11
+        const d = today.getDate();
+        const shift = (d <= 10) ? 1 : 2;          // правило 10/11
+        const min = new Date(y, m + shift, 1);
+        return ymFormat(min.getFullYear(), min.getMonth() + 1);
+    }
+
+
+    function getMonthsMap() {
+        return { ...(window.productActualityConfig?.months || {}) };
+    }
+
+
+    function extendMonthsMap(monthsMap, nextYears = 1) {
+        const keys = Object.keys(monthsMap).sort(); 
+        if (keys.length === 0) return monthsMap;
+
+        const last = keys[keys.length - 1]; 
+        const totalAppend = nextYears * 12;
+
+        let cur = last;
+        for (let i = 0; i < totalAppend; i++) {
+            cur = ymAdd(cur, 1);
+            if (!monthsMap[cur]) {
+                monthsMap[cur] = cur.replace('-', '-');
+            }
+        }
+        return monthsMap;
+    }
+
+    function buildMonthOptions(monthsMap, minYM) {
+        const keys = Object.keys(monthsMap).sort().filter(k => !minYM || k >= minYM);
+        return keys.map(k => `<option value="${k}">${monthsMap[k]}</option>`).join('');
+    }
+
+    function applyFromMonthLimits(fromSelect, monthsMap) {
+        const minYM = getMinStartMonthByRule();
+
+        const prompt = fromSelect.find('option[value=""]').length ? '<option value="">от</option>' : '';
+        const options = prompt + buildMonthOptions(monthsMap, minYM);
+        const current = fromSelect.val();
+
+        fromSelect.html(options);
+
+        if (current && current >= minYM) {
+            fromSelect.val(current);
+        } else {
+            fromSelect.val('');
+        }
+
+        fromSelect.trigger('change');
+    }
+
+    function syncToWithFrom(fromSelect) {
+        const from = fromSelect.val();
+        const $to = fromSelect.closest('td').find('.to-month');
+        if (!$to.length) return;
+
+        // покажем только значения >= from (и пустую)
+        $to.find('option').each(function(){
+            const val = $(this).val();
+            $(this).toggle(!val || !from || val >= from);
+        });
+
+        const curTo = $to.val();
+        if (from && curTo && curTo < from) {
+            $to.val(from);
+        }
+    }
+
+
 });