src/Resources/views/mouvement/form.html.twig line 1

Open in your IDE?
  1. {% extends "@App/Default/base.html.twig" %}
  2. {% block stylesheets %}
  3. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.4.2/chosen.css">
  4. <link rel="stylesheet" type="text/css" media="screen" href="{{ asset('css/bootstrap-switch.min.css') }}" />
  5. {% endblock %}
  6. {% block content %}
  7. {% form_theme form 'jquery.collection.html.twig' %}
  8. <div class="ibox-content">
  9.     <div class="sk-spinner sk-spinner-wave">
  10.         <div class="sk-rect1"></div>
  11.         <div class="sk-rect2"></div>
  12.         <div class="sk-rect3"></div>
  13.         <div class="sk-rect4"></div>
  14.         <div class="sk-rect5"></div>
  15.     </div>
  16.     {{ form_start(form) }}
  17.     <div class="row mb-2">
  18.         <div class="col-md-5">
  19.             <div class="row mb-2">
  20.                 <div class="col-md-6 mb-1">{{ form_row(form.motifMouvement) }}</div>
  21.                 <div class="col-md-6" id="autre-motif">{{ form_row(form.autreMotif) }}</div>
  22.             </div>
  23.             <div class="row mb-2" id="agence-depart">
  24.                 <div class="col-md-6">{{ form_row(form.agenceDepart) }}</div>
  25.             </div>
  26.             <div class="row mb-2">
  27.                 <div class="col-md-6">{{ form_row(form.typeEmplacementDepart) }}</div>
  28.                 <div class="col-md-6">
  29.                     <p><strong>{{ "Emplacement de départ"|trans }}</strong></p>
  30.                     <p id="local-depart">{{ form_widget(form.localDepart) }}</p>
  31.                     <p id="nr-depart">{{ form_widget(form.nanoReseauDepart) }}</p>
  32.                     <p id="client-depart">{{ form_widget(form.clientDepart) }}</p>
  33.                     <p id="user-depart">{{ form_widget(form.userDepart) }}</p>
  34.                 </div>
  35.             </div>
  36.         </div>
  37.         <div class="col-md-2 text-center">
  38.             <div class="col-md-12" style="margin-top: 50%;"><i class="fa fa-4x fa-arrow-right"></i></div>
  39.         </div>
  40.         <div class="col-md-5">
  41.             <div class="row mb-2">
  42.                 <div class="col-md-6">{{ "Date du mouvement"|trans }}</div>
  43.                 <div class="col-md-6">{{ form_widget(form.date) }}</div>
  44.             </div>
  45.             <div class="row mb-2" id="agence-arrivee">
  46.                 <div class="col-md-6">{{ "Agence d'arrivée"|trans }}</div>
  47.                 <div class="col-md-6">{{ form_widget(form.agenceArrivee) }}</div>
  48.             </div>
  49.             <div class="row mb-2">
  50.                 <div class="col-md-6">{{ form_row(form.typeEmplacementArrivee) }}</div>
  51.                 <div class="col-md-6">
  52.                     <p><strong>{{ "Emplacement d'arrivée"|trans }}</strong></p>
  53.                     <p id="local-arrivee">{{ form_widget(form.localArrivee) }}</p>
  54.                     <p id="nr-arrivee">{{ form_widget(form.nanoReseauArrivee) }}</p>
  55.                     <p id="client-arrivee">{{ form_widget(form.clientArrivee) }}</p>
  56.                     <p id="user-arrivee">{{ form_widget(form.userArrivee) }}</p>
  57.                 </div>
  58.             </div>
  59.         </div>
  60.     </div>
  61.     {# Materiels #}
  62.     <div style="position: relative;">
  63.         <div class="row mb-1">
  64.             <h3 class="mb-2 col-md-12">{{ "Liste des matériels" | trans }}</h3>
  65.             <div class="col-md-4">{{ "Model" | trans }}</div>
  66.             <div class="col-md-3">{{ "Identifiants" | trans }}</div>
  67.             <div class="col-md-2">{{ "Quantité" | trans }}</div>
  68.             <div class="col-md-2">{{ "Quantité disponible" | trans }}</div>
  69.             <div class="col-md-1"></div>
  70.         </div>
  71.         {{ form_row(form.materielMouvements) }}
  72.     </div>
  73.     <div class="row mb-2">
  74.         <div class="col-md-12">{{ form_row(form.commentaire) }}</div>
  75.     </div>
  76.     <div class="modal inmodal in" id="validation-modal" tabindex="-1" role="dialog" aria-hidden="true">
  77.         <div class="modal-dialog modal-lg">
  78.             <div class="modal-content fadeIn">
  79.                 <div class="modal-header">
  80.                     <h3></h3>
  81.                 </div>
  82.                 <div class="modal-body">
  83.                     {{ form_row(form.gestionnaire) }}
  84.                     {{ form_row(form.codeSecret) }}
  85.                 </div>
  86.                 <div class="modal-footer">
  87.                     <button class="btn btn-success" type="submit" name="btn_submit" value="1">
  88.                         <i class="fa fa-save"></i>&nbsp;
  89.                         {% trans %}Save and validate{% endtrans %}
  90.                     </button>
  91.                     <button type="button" class="btn btn-white" data-dismiss="modal">
  92.                         {% trans %}Close{% endtrans %}
  93.                         <i class="fa fa-times"></i>
  94.                     </button>
  95.                 </div>
  96.             </div>
  97.         </div>
  98.     </div>
  99.     <div class="hr-line-dashed"></div>
  100.     {% if not mouvement.valide %}
  101.     <div class="form-group text-center">
  102.         <a class="btn btn-white" href="{{ path('app.mouvement.list') }}">
  103.             <i class="fa fa-times"></i>
  104.             {% trans %}Cancel{% endtrans %}
  105.         </a>
  106.         <button class="btn btn-success" type="button" id="validation-btn" name="btn_submit" value="1">
  107.             <i class="fa fa-save"></i>&nbsp;
  108.             {% trans %}Save and validate{% endtrans %}
  109.         </button>
  110.         <button class="btn btn-primary" type="submit" name="btn_submit" value="0">
  111.             <i class="fa fa-save"></i>&nbsp;
  112.             {% trans %}Save{% endtrans %}
  113.         </button>
  114.     </div>
  115.     {% endif %}
  116.     {{ form_end(form) }}
  117. </div>
  118. {% endblock %}
  119. {% block javascripts %}
  120. <script src="{{ asset('/js/jquery.collection.js') }}"></script>
  121. <script src="{{ asset('js/profileForm.js') }}"></script>
  122. <script src="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.4.2/chosen.jquery.js"></script>
  123. <script type="text/javascript">
  124.     $('#validation-btn').click(function(){
  125.         $('#validation-modal').modal('show');
  126.     });
  127.     $(document).ready(function () {
  128.         initChosen();
  129.         // Initialiser les champs en fonction de la valeur existante (édition)
  130.         $('#mouvement_form_motifMouvement').trigger('change');
  131.         $('#mouvement_form_agenceDepart').trigger('change');
  132.         $('#mouvement_form_agenceArrivee').trigger('change');
  133.         // $('.stock_select').trigger('change');
  134.     });
  135.     $('body').on('click', async function () {
  136.         initChosen();
  137.     });
  138.     function initChosen() {
  139.         $(".chosen-select").chosen();
  140.     }
  141.     $('#mouvement_form_materielMouvements').collection({
  142.         allow_up: false,
  143.         allow_down: false,
  144.         hide_useless_buttons: true,
  145.         init_with_n_elements: 1,
  146.         min: 1,
  147.         add: '<a href="#" class="collection-add btn btn-primary" style="position: absolute; top: -1rem; left: 25rem;"><span class="glyphicon glyphicon-plus-sign"></span></a>',
  148.         remove: '',
  149.         add_at_the_end: true,
  150.         after_add: function (collection, element) {
  151.             // initChosen();
  152.         }
  153.     });
  154.     const route = {
  155.         locaux: (agenceId) => Routing.generate('app.locaux_by_agence.get', { 'id': agenceId }),
  156.         nrs: (agenceId) => Routing.generate('app.nr_by_agence.get', { 'id': agenceId }),
  157.         clients: (agenceId) => Routing.generate('app.customers_by_agence.get', { 'id': agenceId }),
  158.         users: (agenceId) => Routing.generate('app.users_by_agence.get', { 'id': agenceId }),
  159.         stocksByLocal: (localId) => Routing.generate('app.locaux_by_agence.get', { 'id': agenceId }),
  160.         stockIdentifiants: (stockId) => Routing.generate('app.ids_from_stock.get', { 'id': stockId }),
  161.     };
  162.     const fillSelect = ($select, items, placeholder = 'Choisir', chosen = false) => {
  163.         $select.empty().append(new Option(placeholder, ''));
  164.         items.forEach(i => $select.append(new Option(i.label, i.id)));
  165.         if (chosen) $select.trigger('chosen:updated');
  166.     };
  167.     // Dépendances agences -> (locaux, nrs, clients, users)
  168.     $('#mouvement_form_agenceDepart').on('change', async function () {
  169.         const id = this.value;
  170.         if (!id) return;
  171.         await Promise.all([
  172.             loadInto(route.locaux(id), '#mouvement_form_localDepart', true),
  173.             loadInto(route.nrs(id), '#mouvement_form_nanoReseauDepart', true),
  174.             loadInto(route.clients(id), '#mouvement_form_clientDepart', true),
  175.             loadInto(route.users(id), '#mouvement_form_userDepart', true),
  176.         ]);
  177.         const motif = $('#mouvement_form_motifMouvement').val();
  178.         if (parseInt(motif) == 1) {
  179.             $('#mouvement_form_agenceArrivee').val(id);
  180.             $('#mouvement_form_agenceArrivee').trigger('change');
  181.             $('#mouvement_form_agenceArrivee').prop('readonly', true);
  182.             $('#mouvement_form_agenceArrivee').hide();
  183.         } else {
  184.             $('#mouvement_form_agenceArrivee').show();
  185.         }
  186.     });
  187.     $('#mouvement_form_agenceArrivee').on('change', async function () {
  188.         const id = this.value;
  189.         if (!id) return;
  190.         await Promise.all([
  191.             loadInto(route.locaux(id), '#mouvement_form_localArrivee', true),
  192.             loadInto(route.nrs(id), '#mouvement_form_nanoReseauArrivee', true),
  193.             loadInto(route.clients(id), '#mouvement_form_clientArrivee', true),
  194.             loadInto(route.users(id), '#mouvement_form_userArrivee', true),
  195.         ]);
  196.     });
  197.     // Local départ -> stocks de la collection[0] au départ (l'utilisateur peut ensuite dupliquer via jquery.collection)
  198.     $('body').on('change', '#mouvement_form_localDepart', async function () {
  199.         const localId = this.value; if (!localId) return;
  200.         const res = await fetch(route.stocksByLocal(localId));
  201.         const stocks = await res.json();
  202.         // Remplit le premier item existant; les suivants se dupliqueront
  203.         fillSelect($('#mouvement_form_materielMouvements_0_stock'), stocks, 'Choisir', true);
  204.     });
  205.     // Sur changement de stock: maj qteDisponible + identifiants
  206.     $('body').on('change', '.stock_select, [id$="_stock"]', async function () {
  207.         const $stock = $(this);
  208.         const stockId = $stock.val(); if (!stockId) return;
  209.         // Récupérer qte (depuis option si chargée via /ajax/stocks) sinon via attribut data-qte
  210.         const qte = parseInt($stock.find(':selected').data('qte') ?? 0, 10);
  211.         const idAttr = $stock.attr('id');
  212.         const pos = idAttr.match(/materielMouvements_(\d+)_stock/)[1];
  213.         // maj qteDisponible + contraintes
  214.         const $qteDispo = $(`#mouvement_form_materielMouvements_${pos}_qteDisponible`);
  215.         const $qte = $(`#mouvement_form_materielMouvements_${pos}_qte`);
  216.         const $ids = $(`#mouvement_form_materielMouvements_${pos}_identifiants`);
  217.         // Charger identifiants
  218.         const data = await (await fetch(route.stockIdentifiants(stockId))).json();
  219.         if ($stock.find(':selected').data('individuel') == '0') {
  220.             // $ids.prop('disabled', true);
  221.             let quantite = data.qte;
  222.             $qteDispo.val(quantite);
  223.             $qte.val(0).attr('max', quantite);
  224.         } else {
  225.             const ids = data.ids;
  226.             $qteDispo.val(ids.length);
  227.             $qte.val(0).attr('max', ids.length);
  228.             $qte.val(0).attr('min', 1);
  229.             $qte.prop('readonly', true);
  230.             fillSelect($ids, ids, 'Choisir', true);
  231.             // $ids.prop('disabled', qte <= 1);
  232.         }
  233.     });
  234.     // Synchroniser qte avec nombre d'identifiants sélectionnés (si multi)
  235.     $('body').on('change', '.identifiant_select', function () {
  236.         const id = $(this).attr('id');
  237.         const pos = id.match(/materielMouvements_(\d+)_identifiants/)[1];
  238.         const nb = ($(this).val() || []).length;
  239.         $(`#mouvement_form_materielMouvements_${pos}_qte`).val(nb);
  240.     });
  241.     function loadInto(url, selector, chosen = false) {
  242.         $.ajax({
  243.             url: url,
  244.             method: 'GET',
  245.             dataType: 'json',
  246.             success: function (data, textStatus, jqXHR) {
  247.                 // Remplir le select
  248.                 fillSelect($(selector), data, 'Choisir', chosen);
  249.             },
  250.             error: function (jqXHR, textStatus, errorThrown) {
  251.                 console.error('[loadInto] ERREUR AJAX');
  252.                 console.error('[loadInto] Status:', textStatus);
  253.                 console.error('[loadInto] Code HTTP:', jqXHR.status);
  254.                 console.error('[loadInto] Message:', errorThrown);
  255.                 console.error('[loadInto] Réponse brute:', jqXHR.responseText);
  256.             },
  257.         });
  258.     }
  259.     $('#mouvement_form_motifMouvement').on('change', function () {
  260.         const motif = parseInt($(this).val());
  261.         // Cache tous les blocs
  262.         $('#local-depart, #local-arrivee, #nr-depart, #nr-arrivee, #client-depart, #client-arrivee, #user-depart, #user-arrivee, #autre-motif').hide();
  263.         // Désactive toutes les radios
  264.         $('#mouvement_form_typeEmplacementDepart input[type="radio"], #mouvement_form_typeEmplacementArrivee input[type="radio"]').prop('disabled', true).prop('checked', false);
  265.         // Réinitialise "required" partout
  266.         $('#mouvement_form input, #mouvement_form select').not('#mouvement_form_commentaire, [id*="identifiants"]').prop('required', false);
  267.         // Fonction utilitaire : affiche un bloc et rend ses champs requis
  268.         function showAndRequire(selector) {
  269.             $(selector).show();
  270.             // $(selector).find('select, input').not('#mouvement_form_commentaire, [id*="identifiants"]').prop('required', true);
  271.         }
  272.         switch (motif) {
  273.             case 1:
  274.                 showAndRequire('#local-depart');
  275.                 showAndRequire('#local-arrivee');
  276.                 $('#mouvement_form_typeEmplacementDepart_1, #mouvement_form_typeEmplacementArrivee_1').prop('checked', true).prop('disabled', false);
  277.                 break;
  278.             case 2:
  279.                 showAndRequire('#local-depart');
  280.                 showAndRequire('#user-arrivee');
  281.                 $('#mouvement_form_typeEmplacementDepart_1, #mouvement_form_typeEmplacementArrivee_2').prop('checked', true).prop('disabled', false);
  282.                 break;
  283.             case 3:
  284.                 showAndRequire('#user-depart');
  285.                 showAndRequire('#nr-arrivee');
  286.                 $('#mouvement_form_typeEmplacementDepart_2, #mouvement_form_typeEmplacementArrivee_4').prop('checked', true).prop('disabled', false);
  287.                 break;
  288.             case 4:
  289.                 showAndRequire('#nr-depart');
  290.                 showAndRequire('#nr-arrivee');
  291.                 $('#mouvement_form_typeEmplacementDepart_4, #mouvement_form_typeEmplacementArrivee_4').prop('checked', true).prop('disabled', false);
  292.                 break;
  293.             case 5:
  294.                 showAndRequire('#local-depart');
  295.                 showAndRequire('#nr-arrivee');
  296.                 $('#mouvement_form_typeEmplacementDepart_1, #mouvement_form_typeEmplacementArrivee_4').prop('checked', true).prop('disabled', false);
  297.                 break;
  298.             case 6:
  299.                 showAndRequire('#nr-depart');
  300.                 showAndRequire('#nr-arrivee');
  301.                 $('#mouvement_form_typeEmplacementDepart_2, #mouvement_form_typeEmplacementArrivee_1').prop('checked', true).prop('disabled', false);
  302.                 break;
  303.             case 7:
  304.                 showAndRequire('#local-depart');
  305.                 showAndRequire('#nr-arrivee');
  306.                 $('#mouvement_form_typeEmplacementDepart_1, #mouvement_form_typeEmplacementArrivee_2').prop('checked', true).prop('disabled', false);
  307.                 break;
  308.             case 8:
  309.                 showAndRequire('#local-depart');
  310.                 $('#mouvement_form_typeEmplacementDepart_1').prop('checked', true).prop('disabled', false);
  311.                 break;
  312.             default:
  313.                 showAndRequire('#autre-motif');
  314.                 showAndRequire('#local-depart');
  315.                 showAndRequire('#local-arrivee');
  316.                 $('#mouvement_form_typeEmplacementDepart_1, #mouvement_form_typeEmplacementArrivee_1').prop('checked', true).prop('disabled', false);
  317.                 break;
  318.         }
  319.     });
  320. </script>
  321. {% endblock %}