Commit 311e343b authored by Stephane Bouvry's avatar Stephane Bouvry
Browse files

Ajout de l'affectation rapide (permet d'ajouter des personnes / organisations)...

Ajout de l'affectation rapide (permet d'ajouter des personnes / organisations) directement depuis la fiche activité
parent c427142c
Loading
Loading
Loading
Loading
Loading
+117 −2
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ use Oscar\Entity\Person;
use Oscar\Entity\Project;
use Oscar\Entity\ProjectMember;
use Oscar\Entity\ProjectPartner;
use Oscar\Entity\Role;
use Oscar\Entity\SpentTypeGroup;
use Oscar\Entity\TabDocument;
use Oscar\Entity\ValidationPeriod;
@@ -850,6 +851,99 @@ class ProjectGrantController extends AbstractOscarController implements UseNotif
    ////////////////////////////////////////////////////////////////////////////
    // ACTIONS
    ////////////////////////////////////////////////////////////////////////////

    private function getProjectFormPersonRoles(): array
    {
        $configured = $this->getOscarConfigurationService()->getActivityFormPersonRoles();
        $configuredIds = is_array($configured) && array_key_exists('roleids', $configured) && is_array($configured['roleids'])
            ? $configured['roleids']
            : [];

        $available = $this->getOscarUserContextService()->getAvailabledRolesPersonActivity();
        $roles = [];
        foreach ($configuredIds as $roleId) {
            $id = (string)$roleId;
            if (!array_key_exists($id, $available) && !array_key_exists((int)$roleId, $available)) {
                continue;
            }
            $label = array_key_exists($id, $available) ? $available[$id] : $available[(int)$roleId];
            $roles[] = [
                'id' => (int)$roleId,
                'label' => (string)$label,
            ];
        }
        return $roles;
    }

    private function getProjectFormOrganizationRoles(): array
    {
        $configured = $this->getOscarConfigurationService()->getActivityFormOrganizationRoles();
        $configuredIds = is_array($configured) && array_key_exists('roleids', $configured) && is_array($configured['roleids'])
            ? $configured['roleids']
            : [];

        $available = $this->getOscarUserContextService()->getAvailabledRolesOrganizationActivity();
        $roles = [];
        foreach ($configuredIds as $roleId) {
            $id = (string)$roleId;
            if (!array_key_exists($id, $available) && !array_key_exists((int)$roleId, $available)) {
                continue;
            }
            $label = array_key_exists($id, $available) ? $available[$id] : $available[(int)$roleId];
            $roles[] = [
                'id' => (int)$roleId,
                'label' => (string)$label,
            ];
        }
        return $roles;
    }

    private function applyProjectFormRoleAssignments(Activity $activity, array $postedValues): void
    {
        $this->getLoggerService()->info('Applying project form role assignments');
        $personSelections = $postedValues['project_form_person_roles'] ?? [];
        if (is_array($personSelections)) {
            $this->getLoggerService()->info('Processing person selections');
            foreach ($personSelections as $roleId => $personId) {
                if (!$roleId || !$personId) {
                    continue;
                }
                /** @var Role|null $role */
                $role = $this->getEntityManager()->getRepository(Role::class)->find((int)$roleId);
                /** @var Person|null $person */
                $person = $this->getEntityManager()->getRepository(Person::class)->find((int)$personId);
                if (!$role || !$person) {
                    continue;
                }
                $this->getPersonService()->personActivityAdd($activity, $person, $role);
            }
        }

        $organizationSelections = $postedValues['project_form_organization_roles'] ?? [];
        if (is_array($organizationSelections)) {
            $this->getLoggerService()->info('Processing organization selections');
            foreach ($organizationSelections as $roleId => $organizationId) {
                if (!$roleId || !$organizationId) {
                    continue;
                }

                /** @var OrganizationRole|null $organizationRole */
                $organizationRole = $this->getEntityManager()->getRepository(OrganizationRole::class)->find((int)$roleId);
                /** @var Organization|null $organization */
                $organization = $this->getEntityManager()->getRepository(Organization::class)->find((int)$organizationId);
                if (!$organizationRole || !$organization) {
                    continue;
                }

                if ($activity->hasOrganization($organization, $organizationRole->getRoleId(), false)) {
                    continue;
                }

                $this->getProjectGrantService()->organizationActivityAdd($organization, $activity, $organizationRole);
            }
        }
    }

    /**
     * @return Response
     */
@@ -883,6 +977,7 @@ class ProjectGrantController extends AbstractOscarController implements UseNotif
            $form->setData($request->getPost());
            if ($form->isValid()) {
                $this->getEntityManager()->flush();
                $this->applyProjectFormRoleAssignments($projectGrant, $request->getPost()->toArray());

                if ($projectGrant->getStatus() !== $beforeStatus) {
                    $this->getEventManager()->trigger(
@@ -897,6 +992,7 @@ class ProjectGrantController extends AbstractOscarController implements UseNotif
                $this->getActivityService()->getGearmanJobLauncherService()->triggerUpdateSearchIndexActivity(
                    $projectGrant
                );

                $this->redirect()->toRoute(
                    'contract/show',
                    ['id' => $projectGrant->getId()]
@@ -915,8 +1011,16 @@ class ProjectGrantController extends AbstractOscarController implements UseNotif
                'freeFields'        => $form->getFreeFieldsInfos(),
                'activity'           => $projectGrant,
                'numbers_keys'       => $numerotationKeys,
                'affectations'       => $this->getProjectGrantApiService()->getActivityJson(
                            $projectGrant->getId(),
                            $this->url(), null,
                            'persons,organizations'
                    )['datas']
                ,
                'allowNodeSelection' => $this->getOscarConfigurationService()->isAllowNodeSelection(),
                "tree"               => $this->getPersonService()->getProjectGrantService()->getActivityTypesTree()
                "tree"               => $this->getPersonService()->getProjectGrantService()->getActivityTypesTree(),
                'projectFormPersonRoles' => $this->getProjectFormPersonRoles(),
                'projectFormOrganizationRoles' => $this->getProjectFormOrganizationRoles(),
            ]
        );
        $view->setTemplate('oscar/project-grant/form');
@@ -1344,6 +1448,7 @@ class ProjectGrantController extends AbstractOscarController implements UseNotif
                    $this->getEntityManager()->flush($projectOrganization);
                }

                $this->applyProjectFormRoleAssignments($projectGrant, $request->getPost()->toArray());

                // Mise à jour de l'index de recherche
                $this->getActivityService()->jobSearchUpdate($projectGrant);
@@ -1365,7 +1470,9 @@ class ProjectGrantController extends AbstractOscarController implements UseNotif
                'activity'         => $projectGrant,
                'project'          => null,
                'numerotationKeys' => $numerotationKeys,
                'numbers_keys'     => $numerotationKeys
                'numbers_keys'     => $numerotationKeys,
                'projectFormPersonRoles' => $this->getProjectFormPersonRoles(),
                'projectFormOrganizationRoles' => $this->getProjectFormOrganizationRoles(),
            ]
        );

@@ -1528,6 +1635,7 @@ class ProjectGrantController extends AbstractOscarController implements UseNotif
                        );
                    }
                }
                $this->applyProjectFormRoleAssignments($projectGrant, $request->getPost()->toArray());

                // Mise à jour de l'index de recherche
                try {
@@ -1554,10 +1662,17 @@ class ProjectGrantController extends AbstractOscarController implements UseNotif
                'activity'           => $projectGrant,
                'project'            => $project,
                'numerotationKeys'   => $numerotationKeys,
                // Pas d'affectations ici (mais on laisse le tableau pour éviter une erreur JS)
                'affectations'       => [
                        'persons' => [ 'entities' => [] ],
                        'organizations' => [ 'entities' => [] ],
                ],
                'numbers_keys'       => $numerotationKeys,
                'allowNodeSelection' => $this->getOscarConfigurationService()->isAllowNodeSelection(),
                "tree"               => $this->getPersonService()->getProjectGrantService()->getActivityTypesTree(),
                'organizationsCount' => $organizationsCount,
                'projectFormPersonRoles' => $this->getProjectFormPersonRoles(),
                'projectFormOrganizationRoles' => $this->getProjectFormOrganizationRoles(),
            ]
        );

+10 −0
Original line number Diff line number Diff line
@@ -659,6 +659,16 @@ class OscarConfigurationService implements ServiceLocatorAwareInterface

    public function setSpentAccountFilter($stringArray)
    {
        if (is_array($stringArray)) {
            $data = array_values(array_filter(array_map(function ($item) {
                return trim((string)$item);
            }, $stringArray), function ($item) {
                return $item !== '';
            }));
            $this->saveEditableConfKey(self::spents_account_filter, $data);
            return;
        }

        $extract = new DataStringArray();
        $data = $extract->extract($stringArray);
        $this->saveEditableConfKey(self::spents_account_filter, $data);
+47 −37
Original line number Diff line number Diff line
@@ -7,19 +7,17 @@

use Oscar\OscarText;

?>
<link rel="stylesheet" type="text/css"
      href="<?= $this->basePath() . '/js/vendor/bootstrap-datepicker/dist/css/bootstrap-datepicker3.min.css' ?>"/>
<div class="container">
?><div class="container">
    <h1>
        <?php if ($activity->getProject()): ?>
            <small class="text-light">
            <code class="text-light">
                <i class="icon-cubes"></i>
                <?= $activity->getProject() ?>
            </small><br/>
            </code><br/>
        <?php endif; ?>
        <?php if ($activity->getId()): ?>
            <strong><i class="icon-cube"></i> <?= $activity->getLabel() ?></strong> (Modification)
            <strong><i class="icon-cube"></i> <?= $activity->getLabel() ?></strong>
            <small class="text-muted">(Modification)</small>
        <?php else: ?>
            <strong><i class="icon-cube"></i> <?= $this->oscarText("Nouvelle activité") ?></strong>
        <?php endif; ?>
@@ -34,8 +32,9 @@ use Oscar\OscarText;
        <div><?php echo $this->oscarFormRow($form->get('label')); ?></div>

        <?= $this->oscarFormRow($form->get('description')); ?>
        <?php if ($config['motscles']['use']): ?>

        <div class="row">
            <?php if ($config['motscles']['use']): ?>
            <div class="col-md-6">
                <div id="activity-mots-cles"
                     data-url="<?= $this->url('activity-mots-cles/api') ?>"
@@ -55,8 +54,16 @@ use Oscar\OscarText;
                </div>
                <?php echo $this->Vite()->addJs('src/ActivityMotsCles.js'); ?>
            </div>
            <?php endif; ?>
            <?php if ($config['disciplines']['use']): ?>
                <div class="col-md-3">
                    <?php echo $this->formLabel($form->get('disciplines')); ?>
                    <?php echo $this->formSelect($form->get('disciplines')); ?>
                    <?php echo $this->formElementErrors($form->get('disciplines')); ?>
                </div>
            <?php endif; ?>
        </div>


        <?php if ($withOrganization): ?>
            <h3><?= $this->translate("Rôle de votre structure") ?></h3>
@@ -78,16 +85,26 @@ use Oscar\OscarText;
            <?php endforeach; ?>
        <?php endif; ?>

        <?php if (!empty($projectFormPersonRoles) || !empty($projectFormOrganizationRoles)): ?>
            <h3><?= $this->translate("Affectations rapides") ?></h3>
            <div id="project-grant-form-roles"
                 data-person-roles="<?= htmlspecialchars(base64_encode(json_encode($projectFormPersonRoles)), ENT_QUOTES, 'UTF-8') ?>"
                 data-organization-roles="<?= htmlspecialchars(base64_encode(json_encode($projectFormOrganizationRoles)), ENT_QUOTES, 'UTF-8') ?>"
                 data-affectations="<?= htmlspecialchars(base64_encode(json_encode($affectations)), ENT_QUOTES, 'UTF-8') ?>">
            </div>
            <?= $this->Vite()->addJs('src/ProjectGrantFormRoles.js'); ?>
        <?php endif; ?>

        <h3><?= $this->translate("Données administratives") ?></h3>

        <div class="row">
            <div class="col-md-3">
            <div class="col-md-4">
                <?= $this->oscarFormRow($form->get('codeEOTP')); ?>
            </div>
            <div class="col-md-3">
            <div class="col-md-4">
                <?= $this->oscarFormRow($form->get('status')); ?>
            </div>
            <div class="col-md-3">
            <div class="col-md-4">
                <?php echo $this->formLabel($form->get('activityType')); ?>

                <div id="activity-type-select"
@@ -99,13 +116,6 @@ use Oscar\OscarText;
                <?php echo $this->Vite()->addJs('src/ActivityTypeSelect.js'); ?>
                <?php echo $this->formElementErrors($form->get('activityType'), ["class" => "alert alert-danger"]); ?>
            </div>
            <?php if ($config['disciplines']['use']): ?>
                <div class="col-md-3">
                    <?php echo $this->formLabel($form->get('disciplines')); ?>
                    <?php echo $this->formSelect($form->get('disciplines')); ?>
                    <?php echo $this->formElementErrors($form->get('disciplines')); ?>
                </div>
            <?php endif; ?>
        </div>

        <div class="row">
+45 −0
Original line number Diff line number Diff line
import { createApp } from "vue";
import ProjectGrantFormRoles from "./views/ProjectGrantFormRoles.vue";
import {oscarText} from "./utils/OscarText.js";
import PrimeVue from "primevue/config";
import Aura from '@primeuix/themes/aura';

const element = document.querySelector("#project-grant-form-roles");

function parseBase64Json(raw, fallback = []) {
  if (!raw) {
    return fallback;
  }
  try {
    return JSON.parse(atob(raw));
  } catch (_e) {
    return fallback;
  }
}




if (element) {
  const app = createApp(ProjectGrantFormRoles, {
    personRoles: parseBase64Json(element.dataset.personRoles, []),
    organizationRoles: parseBase64Json(element.dataset.organizationRoles, []),
    affectations: parseBase64Json(element.dataset.affectations, []),
  });
  app.use(PrimeVue, {
    theme: {
      preset: Aura
    },
    locale: {
      monthNames:	['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Aout', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
      monthNamesShort:	['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Jun', 'Jui', 'Aou', 'Sep', 'Oct', 'Nov', 'Dec'],
      firstDayOfWeek: 1,
      dayNames:	['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'],
      dayNamesMin:	['Di', 'Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa'],
      dayNamesShort:	['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
      clear: "Vider",
      today: "Aujourd'hui"
    }
  });
  app.mount("#project-grant-form-roles");
}
+2 −1
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@
          forceSelection
          showClear
          class="form-control-autocomplete person-autocomplete"
          placeholder="Rechercher une personne..."
          :placeholder="placeholder"
          @complete="handlerComplete"
          @item-select="handlerItemSelect"
          @clear="handlerClear"
@@ -75,6 +75,7 @@ export default {
  props: {
    value: { default: null },
    url: { default: "/person?l=m&q=", type: String },
    placeholder: { default: "Rechercher une personne..." },
  },

  emits: ["update:value", "change", "input", "personSelected"],
Loading