|
|
@@ -131,28 +131,74 @@
|
|
|
</div>
|
|
|
|
|
|
<div class="mb-3">
|
|
|
- <label for="pricing_code" class="form-label">Шифр расценки</label>
|
|
|
- <div class="row">
|
|
|
- <div class="col-md-5 position-relative">
|
|
|
- <input type="text"
|
|
|
- class="form-control"
|
|
|
- id="pricing_code"
|
|
|
- name="pricing_code"
|
|
|
- value="{{ old('pricing_code', $spare_part->pricing_code ?? '') }}"
|
|
|
- autocomplete="off"
|
|
|
- {{ hasRole('admin') ? '' : 'readonly' }}>
|
|
|
- <div class="autocomplete-dropdown" id="pricing_code_dropdown"></div>
|
|
|
- </div>
|
|
|
- <div class="col-md-7">
|
|
|
- <input type="text"
|
|
|
- class="form-control"
|
|
|
- id="pricing_code_description"
|
|
|
- name="pricing_code_description"
|
|
|
- placeholder="Расшифровка"
|
|
|
- {{ hasRole('admin') ? '' : 'readonly' }}>
|
|
|
- </div>
|
|
|
+ <label class="form-label">Шифр расценки и коды ресурсов</label>
|
|
|
+ <div id="pricing_codes_container">
|
|
|
+ @php
|
|
|
+ $existingCodes = old('pricing_codes', $spare_part ? $spare_part->pricingCodes->pluck('code')->toArray() : []);
|
|
|
+ @endphp
|
|
|
+ @forelse($existingCodes as $index => $code)
|
|
|
+ <div class="pricing-code-row row mb-2" data-index="{{ $index }}">
|
|
|
+ <div class="col-md-5 position-relative">
|
|
|
+ <input type="text"
|
|
|
+ class="form-control pricing-code-input"
|
|
|
+ name="pricing_codes[]"
|
|
|
+ value="{{ $code }}"
|
|
|
+ autocomplete="off"
|
|
|
+ placeholder="Код"
|
|
|
+ {{ hasRole('admin') ? '' : 'readonly' }}>
|
|
|
+ <div class="autocomplete-dropdown pricing-code-dropdown"></div>
|
|
|
+ </div>
|
|
|
+ <div class="col-md-5">
|
|
|
+ <input type="text"
|
|
|
+ class="form-control pricing-code-description-input"
|
|
|
+ name="pricing_codes_descriptions[]"
|
|
|
+ placeholder="Расшифровка"
|
|
|
+ {{ hasRole('admin') ? '' : 'readonly' }}>
|
|
|
+ </div>
|
|
|
+ <div class="col-md-2">
|
|
|
+ @if(hasRole('admin'))
|
|
|
+ <button type="button" class="btn btn-outline-danger btn-remove-pricing-code" title="Удалить">
|
|
|
+ <i class="bi bi-trash"></i>
|
|
|
+ </button>
|
|
|
+ @endif
|
|
|
+ </div>
|
|
|
+ <div class="col-12"><div class="form-text pricing-code-hint"></div></div>
|
|
|
+ </div>
|
|
|
+ @empty
|
|
|
+ <div class="pricing-code-row row mb-2" data-index="0">
|
|
|
+ <div class="col-md-5 position-relative">
|
|
|
+ <input type="text"
|
|
|
+ class="form-control pricing-code-input"
|
|
|
+ name="pricing_codes[]"
|
|
|
+ value=""
|
|
|
+ autocomplete="off"
|
|
|
+ placeholder="Код"
|
|
|
+ {{ hasRole('admin') ? '' : 'readonly' }}>
|
|
|
+ <div class="autocomplete-dropdown pricing-code-dropdown"></div>
|
|
|
+ </div>
|
|
|
+ <div class="col-md-5">
|
|
|
+ <input type="text"
|
|
|
+ class="form-control pricing-code-description-input"
|
|
|
+ name="pricing_codes_descriptions[]"
|
|
|
+ placeholder="Расшифровка"
|
|
|
+ {{ hasRole('admin') ? '' : 'readonly' }}>
|
|
|
+ </div>
|
|
|
+ <div class="col-md-2">
|
|
|
+ @if(hasRole('admin'))
|
|
|
+ <button type="button" class="btn btn-outline-danger btn-remove-pricing-code" title="Удалить">
|
|
|
+ <i class="bi bi-trash"></i>
|
|
|
+ </button>
|
|
|
+ @endif
|
|
|
+ </div>
|
|
|
+ <div class="col-12"><div class="form-text pricing-code-hint"></div></div>
|
|
|
+ </div>
|
|
|
+ @endforelse
|
|
|
</div>
|
|
|
- <div class="form-text" id="pricing_code_hint"></div>
|
|
|
+ @if(hasRole('admin'))
|
|
|
+ <button type="button" class="btn btn-sm btn-outline-primary" id="btn_add_pricing_code">
|
|
|
+ <i class="bi bi-plus-circle"></i> Добавить код
|
|
|
+ </button>
|
|
|
+ @endif
|
|
|
</div>
|
|
|
|
|
|
<div class="mb-3">
|
|
|
@@ -424,18 +470,15 @@ waitForJQuery(function() {
|
|
|
|
|
|
// Инициализация автокомплитов
|
|
|
initAutocomplete('tsn_number', 'tsn_number_dropdown', 'tsn_number_description', 'tsn_number_hint', 'tsn_number');
|
|
|
- initAutocomplete('pricing_code', 'pricing_code_dropdown', 'pricing_code_description', 'pricing_code_hint', 'pricing_code');
|
|
|
|
|
|
// Загрузка расшифровок при открытии страницы
|
|
|
function loadInitialDescription(inputId, descriptionId, hintId, type) {
|
|
|
const code = $('#' + inputId).val().trim();
|
|
|
- //console.log('Load initial for:', inputId, 'code:', code);
|
|
|
if (code.length > 0) {
|
|
|
$.get('{{ route('pricing_codes.get_description') }}', {
|
|
|
type: type,
|
|
|
code: code
|
|
|
}).done(function(data) {
|
|
|
- //console.log('Initial description for', inputId, ':', data);
|
|
|
if (data.description) {
|
|
|
$('#' + descriptionId).val(data.description);
|
|
|
$('#' + descriptionId).prop('readonly', true);
|
|
|
@@ -451,8 +494,209 @@ waitForJQuery(function() {
|
|
|
}
|
|
|
|
|
|
loadInitialDescription('tsn_number', 'tsn_number_description', 'tsn_number_hint', 'tsn_number');
|
|
|
- loadInitialDescription('pricing_code', 'pricing_code_description', 'pricing_code_hint', 'pricing_code');
|
|
|
- //console.log('Autocomplete init complete');
|
|
|
+
|
|
|
+ // Функционал множественных шифров расценки
|
|
|
+ function initPricingCodeAutocomplete($row) {
|
|
|
+ const $input = $row.find('.pricing-code-input');
|
|
|
+ const $dropdown = $row.find('.pricing-code-dropdown');
|
|
|
+ const $description = $row.find('.pricing-code-description-input');
|
|
|
+ const $hint = $row.find('.pricing-code-hint');
|
|
|
+ let currentFocus = -1;
|
|
|
+ let searchTimeout;
|
|
|
+
|
|
|
+ $input.on('input', function() {
|
|
|
+ const query = $(this).val().trim();
|
|
|
+ clearTimeout(searchTimeout);
|
|
|
+
|
|
|
+ if (query.length > 0) {
|
|
|
+ searchTimeout = setTimeout(function() {
|
|
|
+ $.get('{{ route('pricing_codes.search') }}', {
|
|
|
+ type: 'pricing_code',
|
|
|
+ query: query
|
|
|
+ }).done(function(data) {
|
|
|
+ showDropdownPricing($dropdown, data, query, $input, $description, $hint);
|
|
|
+ });
|
|
|
+
|
|
|
+ $.get('{{ route('pricing_codes.get_description') }}', {
|
|
|
+ type: 'pricing_code',
|
|
|
+ code: query
|
|
|
+ }).done(function(data) {
|
|
|
+ if (data.description) {
|
|
|
+ $description.val(data.description);
|
|
|
+ $description.prop('readonly', true);
|
|
|
+ $hint.html('<i class="bi bi-check-circle text-success"></i> Код найден в справочнике');
|
|
|
+ } else {
|
|
|
+ $description.val('');
|
|
|
+ $description.prop('readonly', false);
|
|
|
+ $hint.html('<i class="bi bi-plus-circle text-primary"></i> Новый код - заполните расшифровку');
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }, 200);
|
|
|
+ } else {
|
|
|
+ $dropdown.hide();
|
|
|
+ $description.val('');
|
|
|
+ $description.prop('readonly', false);
|
|
|
+ $hint.text('');
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ $input.on('focus', function() {
|
|
|
+ const query = $(this).val().trim();
|
|
|
+ if (query.length > 0) {
|
|
|
+ $.get('{{ route('pricing_codes.search') }}', {
|
|
|
+ type: 'pricing_code',
|
|
|
+ query: query
|
|
|
+ }, function(data) {
|
|
|
+ showDropdownPricing($dropdown, data, query, $input, $description, $hint);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ $input.on('keydown', function(e) {
|
|
|
+ const $items = $dropdown.find('.autocomplete-item');
|
|
|
+
|
|
|
+ if (e.keyCode === 40) {
|
|
|
+ e.preventDefault();
|
|
|
+ currentFocus++;
|
|
|
+ if (currentFocus >= $items.length) currentFocus = 0;
|
|
|
+ $items.removeClass('active');
|
|
|
+ if (currentFocus >= 0) $items.eq(currentFocus).addClass('active');
|
|
|
+ } else if (e.keyCode === 38) {
|
|
|
+ e.preventDefault();
|
|
|
+ currentFocus--;
|
|
|
+ if (currentFocus < 0) currentFocus = $items.length - 1;
|
|
|
+ $items.removeClass('active');
|
|
|
+ if (currentFocus >= 0) $items.eq(currentFocus).addClass('active');
|
|
|
+ } else if (e.keyCode === 13) {
|
|
|
+ e.preventDefault();
|
|
|
+ if (currentFocus > -1 && $items.length > 0) {
|
|
|
+ $items.eq(currentFocus).trigger('click');
|
|
|
+ }
|
|
|
+ } else if (e.keyCode === 27) {
|
|
|
+ $dropdown.hide();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ $(document).on('click', function(e) {
|
|
|
+ if (!$(e.target).closest($row).length) {
|
|
|
+ $dropdown.hide();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function showDropdownPricing($dropdown, items, query, $input, $description, $hint) {
|
|
|
+ $dropdown.empty();
|
|
|
+
|
|
|
+ if (items.length === 0) {
|
|
|
+ $dropdown.hide();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ items.forEach(function(item) {
|
|
|
+ const $item = $('<div class="autocomplete-item"></div>');
|
|
|
+ const highlightedCode = highlightMatchPricing(item.code, query);
|
|
|
+ const highlightedDesc = item.description ? highlightMatchPricing(item.description, query) : '';
|
|
|
+
|
|
|
+ $item.html('<div class="code">' + highlightedCode + '</div>' +
|
|
|
+ (item.description ? '<div class="description">' + highlightedDesc + '</div>' : ''));
|
|
|
+ $item.data('code', item.code);
|
|
|
+ $item.data('description', item.description || '');
|
|
|
+
|
|
|
+ $item.on('click', function() {
|
|
|
+ $input.val($(this).data('code'));
|
|
|
+ $description.val($(this).data('description'));
|
|
|
+ $description.prop('readonly', true);
|
|
|
+ $hint.html('<i class="bi bi-check-circle text-success"></i> Код найден в справочнике');
|
|
|
+ $dropdown.hide();
|
|
|
+ });
|
|
|
+
|
|
|
+ $dropdown.append($item);
|
|
|
+ });
|
|
|
+
|
|
|
+ $dropdown.show();
|
|
|
+ }
|
|
|
+
|
|
|
+ function highlightMatchPricing(text, query) {
|
|
|
+ if (!text || !query) return text || '';
|
|
|
+ const regex = new RegExp('(' + query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + ')', 'gi');
|
|
|
+ return text.replace(regex, '<strong>$1</strong>');
|
|
|
+ }
|
|
|
+
|
|
|
+ function loadInitialPricingCodeDescription($row) {
|
|
|
+ const code = $row.find('.pricing-code-input').val().trim();
|
|
|
+ const $description = $row.find('.pricing-code-description-input');
|
|
|
+ const $hint = $row.find('.pricing-code-hint');
|
|
|
+
|
|
|
+ if (code.length > 0) {
|
|
|
+ $.get('{{ route('pricing_codes.get_description') }}', {
|
|
|
+ type: 'pricing_code',
|
|
|
+ code: code
|
|
|
+ }).done(function(data) {
|
|
|
+ if (data.description) {
|
|
|
+ $description.val(data.description);
|
|
|
+ $description.prop('readonly', true);
|
|
|
+ $hint.html('<i class="bi bi-check-circle text-success"></i> Код найден в справочнике');
|
|
|
+ } else {
|
|
|
+ $description.prop('readonly', false);
|
|
|
+ $hint.html('<i class="bi bi-plus-circle text-primary"></i> Новый код - заполните расшифровку');
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Инициализация существующих строк
|
|
|
+ $('.pricing-code-row').each(function() {
|
|
|
+ initPricingCodeAutocomplete($(this));
|
|
|
+ loadInitialPricingCodeDescription($(this));
|
|
|
+ });
|
|
|
+
|
|
|
+ // Добавление новой строки
|
|
|
+ $('#btn_add_pricing_code').on('click', function() {
|
|
|
+ const $container = $('#pricing_codes_container');
|
|
|
+ const newIndex = $container.find('.pricing-code-row').length;
|
|
|
+ const $newRow = $(`
|
|
|
+ <div class="pricing-code-row row mb-2" data-index="${newIndex}">
|
|
|
+ <div class="col-md-5 position-relative">
|
|
|
+ <input type="text"
|
|
|
+ class="form-control pricing-code-input"
|
|
|
+ name="pricing_codes[]"
|
|
|
+ value=""
|
|
|
+ autocomplete="off"
|
|
|
+ placeholder="Код">
|
|
|
+ <div class="autocomplete-dropdown pricing-code-dropdown"></div>
|
|
|
+ </div>
|
|
|
+ <div class="col-md-5">
|
|
|
+ <input type="text"
|
|
|
+ class="form-control pricing-code-description-input"
|
|
|
+ name="pricing_codes_descriptions[]"
|
|
|
+ placeholder="Расшифровка">
|
|
|
+ </div>
|
|
|
+ <div class="col-md-2">
|
|
|
+ <button type="button" class="btn btn-outline-danger btn-remove-pricing-code" title="Удалить">
|
|
|
+ <i class="bi bi-trash"></i>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ <div class="col-12"><div class="form-text pricing-code-hint"></div></div>
|
|
|
+ </div>
|
|
|
+ `);
|
|
|
+ $container.append($newRow);
|
|
|
+ initPricingCodeAutocomplete($newRow);
|
|
|
+ $newRow.find('.pricing-code-input').focus();
|
|
|
+ });
|
|
|
+
|
|
|
+ // Удаление строки
|
|
|
+ $(document).on('click', '.btn-remove-pricing-code', function() {
|
|
|
+ const $container = $('#pricing_codes_container');
|
|
|
+ if ($container.find('.pricing-code-row').length > 1) {
|
|
|
+ $(this).closest('.pricing-code-row').remove();
|
|
|
+ } else {
|
|
|
+ // Если осталась одна строка, просто очищаем её
|
|
|
+ const $row = $(this).closest('.pricing-code-row');
|
|
|
+ $row.find('.pricing-code-input').val('');
|
|
|
+ $row.find('.pricing-code-description-input').val('').prop('readonly', false);
|
|
|
+ $row.find('.pricing-code-hint').text('');
|
|
|
+ }
|
|
|
+ });
|
|
|
@endif
|
|
|
});
|
|
|
});
|