}
}
- 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')
}
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 = [];
([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(){
<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">
</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(){
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);
+ }
+ }
+
+
});