Commit 611fdc97 authored by Stephane Bouvry's avatar Stephane Bouvry
Browse files

- Importation des Lots de travail dans les activités (Via les fichiers JSON)

- Importation des créneaux de déclaration (v1)
- Ajout de données Lots de travail / créneaux dans les données de démo
- Mise en page de la synthèse des feuilles de temps (largeur des colonnes + affichage des codes des lots de travail)
- Docker : Type de créneaux Hors-lot par défaut
parent 7c450051
Loading
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -17,6 +17,16 @@ return array(
        // ! EXPERIMENTAL !
        'generated-documents' => [],

        'horslots' => [
            ////////////////////////////////// EXEMPLE de CONFIGURATION DES HORS-LOTS
            'conges' => [ 'code' => 'conges',  'label' => 'Congés', 'group' => 'abs', 'description' => 'Congès/RTT', 'icon' => true ],
            'teaching' => [ 'code' => 'teaching',  'label' => 'Enseignement', 'group' => 'education', 'description' => 'Cours', 'icon' => true ],
            'sickleave' => [ 'code' => 'sickleave', 'label' => 'Arrêt maladie', 'group' => 'abs', 'description' => '', 'icon' => true ],
            'research' => [ 'code' => 'research', 'label' => 'Autre recherche', 'group' => 'research', 'description' => 'Recherche hors-contrat', 'icon' => true ],
            'other' => [ 'code' => 'other', 'label' => 'Divers', 'group' => 'other', 'description' => 'Autre activité', 'icon' => true ],
        ],


        // Répartition horaire
        'declarationsDurations' => [
            'dayLength'     => [
+5 −5
Original line number Diff line number Diff line
@@ -37,15 +37,15 @@
        </div>

        <div v-for="wp in initialdata.headings.current.workpackages" class="main research">
          <span class="value hours">{{ wp.label }}</span>
          <span class="header label">{{ wp.code }}</span>
        </div>

        <div class="main research total">
          <span class="value hours">Total</span>
          <span class="header label">Total</span>
        </div>

        <div v-for="prj in initialdata.headings.prjs.prjs" :title="prj.label"  class="research">
          <span class="value hours">{{ prj.label }}</span>
          <span class="header label">{{ prj.label }}</span>
        </div>

        <div v-for="other in initialdata.headings.others"
@@ -53,11 +53,11 @@
             :class="other.group"
             v-if="other.label != 'Invalid' || (other.label == 'Invalid' && initialdata.headings.has_invalid)"
        >
          <span class="value hours">{{ other.label }}</span>
          <span class="header label">{{ other.label.substr(0,16) }}</span>
        </div>

        <div class="total">
          <span class="value">
          <span class="header label">
            Total
          </span>
        </div>
+37 −8
Original line number Diff line number Diff line
@@ -46,13 +46,13 @@
    "projectlabel": "Théorie de la relativité",
    "description": "Description le d'activité 2",
    "label": "Exemple d'activité 2",
    "datestart": "2015-01-01",
    "dateend": "2017-12-31",
    "datesigned": "2015-02-01",
    "datestart": "2021-01-01",
    "dateend": "2022-12-31",
    "datesigned": "2021-01-05",
    "pfi": "",
    "type": "Colloques",
    "amount": 15000,
    "financialImpact": "Aucune",
    "financialImpact": "Recettes",
    "milestones": [
    ],
    "payments": [
@@ -112,9 +112,9 @@
    "acronym": "MECAQUAN",
    "projectlabel": "Mécanique quantique",
    "label": "Étude des quanta",
    "datestart": "2012-01-01",
    "dateend": "2020-12-31",
    "datesigned": "2014-02-01",
    "datestart": "2021-01-01",
    "dateend": "2022-12-31",
    "datesigned": "2021-02-01",
    "financialImpact": "Dépense",
    "pfi": "",
    "type": "ANR",
@@ -142,6 +142,35 @@
        "Marcel Grossmann",
        "Sarah Déclarant"
      ]
    },
    "workpackages": [
      {
        "code": "WP1",
        "label": "Travaux préparatoires",
        "declarers": [
          "Max Plank",
          "Erwin Schrödinger",
          "Marcel Grossmann",
          "Sarah Déclarant"
        ]
      },
      {
        "code": "WP2",
        "label": "Rédaction d'article",
        "declarers": [
          "Max Plank",
          "Erwin Schrödinger",
          "Marcel Grossmann"
        ]
      },
      {
        "code": "WP3",
        "label": "Expériences",
        "declarers": [
          "Marcel Grossmann",
          "Sarah Déclarant"
        ]
      }
    ]
  }
]
 No newline at end of file
+201 −6
Original line number Diff line number Diff line
[
  {
    "declarer" : "",
    "declarer_id" : 1,
    "workpackage_code" : "",
    "activity_acronym" : "",
    "date": "",
    "duration": ""
    "uid": "imported-ts-01",
    "declarer" : "Albert Einstein",
    "declarer_email" : "albert.einstein@jacksay.com",
    "workpackage_code" : "WP1",
    "other_key" : null,
    "activity_pfi" : "",
    "activity_uid" : "A0002",
    "date": "2022-01-03",
    "duration": 8.0,
    "comment": "Activité importée"
  },
  {
    "uid": "imported-ts-02",
    "declarer" : "Albert Einstein",
    "declarer_email" : "albert.einstein@jacksay.com",
    "workpackage_code" : "WP1",
    "other_key" : null,
    "activity_pfi" : "",
    "activity_uid" : "A0002",
    "date": "2022-01-04",
    "duration": 4.0,
    "comment": "Activité importée"
  },
  {
    "uid": "imported-ts-03",
    "declarer" : "Albert Einstein",
    "declarer_email" : "albert.einstein@jacksay.com",
    "workpackage_code" : "WP2",
    "other_key" : null,
    "activity_pfi" : "",
    "activity_uid" : "A0002",
    "date": "2022-01-04",
    "duration": 4.0,
    "comment": "Activité importée"
  },
  {
    "uid": "imported-ts-04",
    "declarer" : "Albert Einstein",
    "declarer_email" : "albert.einstein@jacksay.com",
    "workpackage_code" : null,
    "other_key" : "conges",
    "activity_pfi" : null,
    "activity_uid" : null,
    "date": "2022-01-05",
    "duration": 8.0,
    "comment": ""
  },
  {
    "uid": "imported-ts-05",
    "declarer" : "Albert Einstein",
    "declarer_email" : "albert.einstein@jacksay.com",
    "workpackage_code" : null,
    "other_key" : "research",
    "activity_pfi" : null,
    "activity_uid" : null,
    "date": "2022-01-06",
    "duration": 8.0,
    "comment": ""
  },
  {
    "uid": "imported-ts-06",
    "declarer" : "Albert Einstein",
    "declarer_email" : "albert.einstein@jacksay.com",
    "workpackage_code" : "WP3",
    "other_key" : null,
    "activity_pfi" : "",
    "activity_uid" : "A0002",
    "date": "2022-01-07",
    "duration": 8.0,
    "comment": "Activité importée"
  },
  {
    "uid": "imported-ts-07",
    "declarer" : "Albert Einstein",
    "declarer_email" : "albert.einstein@jacksay.com",
    "workpackage_code" : "WP3",
    "other_key" : null,
    "activity_pfi" : "",
    "activity_uid" : "A0002",
    "date": "2022-01-10",
    "duration": 8.0,
    "comment": "Activité importée"
  }
,
  {
    "uid": "imported-ts-08",
    "declarer" : "Marcel Grossmann",
    "declarer_email" : "marcel.grossmann@jacksay.com",
    "workpackage_code" : "WP1",
    "other_key" : null,
    "activity_pfi" : "",
    "activity_uid" : "A0002",
    "date": "2022-01-03",
    "duration": 8.0,
    "comment": "Activité importée"
  }
,
  { "uid": "imported-ts-09",
    "declarer" : "Marcel Grossmann", "declarer_email" : "marcel.grossmann@jacksay.com",
    "workpackage_code" : "WP1", "activity_pfi" : "", "activity_uid" : "A0003",
    "other_key" : null,
    "date": "2022-01-04", "duration": 8.0,
    "comment": "Activité importée"
  },
  { "uid": "imported-ts-10",
    "declarer" : "Marcel Grossmann", "declarer_email" : "marcel.grossmann@jacksay.com",
    "workpackage_code" : "WP3", "activity_pfi" : "", "activity_uid" : "A0003",
    "other_key" : null,
    "date": "2022-01-05", "duration": 8.0,
    "comment": "Activité importée"
  },
  { "uid": "imported-ts-11",
    "declarer" : "Marcel Grossmann", "declarer_email" : "marcel.grossmann@jacksay.com",
    "workpackage_code" : "WP2", "activity_pfi" : "", "activity_uid" : "A0003",
    "other_key" : null,
    "date": "2022-01-06", "duration": 8.0,
    "comment": "Activité importée"
  },
  { "uid": "imported-ts-12",
    "declarer" : "Marcel Grossmann", "declarer_email" : "marcel.grossmann@jacksay.com",
    "workpackage_code" : "WP1", "activity_pfi" : "", "activity_uid" : "A0003",
    "other_key" : null,
    "date": "2022-01-07", "duration": 2.0,
    "comment": "Activité importée"
  },
  { "uid": "imported-ts-13",
    "declarer" : "Marcel Grossmann", "declarer_email" : "marcel.grossmann@jacksay.com",
    "workpackage_code" : "WP3", "activity_pfi" : "", "activity_uid" : "A0003",
    "other_key" : null,
    "date": "2022-01-07", "duration": 6.0,
    "comment": "Activité importée"
  }


  ,
  { "uid": "imported-ts-14",
    "declarer" : "Marcel Grossmann", "declarer_email" : "marcel.grossmann@jacksay.com",
    "workpackage_code" : null, "activity_pfi" : "", "activity_uid" : null,
    "other_key" : "teaching",
    "date": "2022-01-10", "duration": 2.0,
    "comment": "Cours"
  },
  { "uid": "imported-ts-15",
    "declarer" : "Marcel Grossmann", "declarer_email" : "marcel.grossmann@jacksay.com",
    "workpackage_code" : null, "activity_pfi" : "", "activity_uid" : null,
    "other_key" : "teaching",
    "date": "2022-01-11", "duration": 4.0,
    "comment": "Cours"
  },
  { "uid": "imported-ts-16",
    "declarer" : "Marcel Grossmann", "declarer_email" : "marcel.grossmann@jacksay.com",
    "workpackage_code" : null, "activity_pfi" : "", "activity_uid" : null,
    "other_key" : "teaching",
    "date": "2022-01-12", "duration": 2.0,
    "comment": "Cours"
  },
  { "uid": "imported-ts-17",
    "declarer" : "Marcel Grossmann", "declarer_email" : "marcel.grossmann@jacksay.com",
    "workpackage_code" : null, "activity_pfi" : "", "activity_uid" : null,
    "other_key" : "teaching",
    "date": "2022-01-13", "duration": 8.0,
    "comment": "Cours"
  },
  { "uid": "imported-ts-18",
    "declarer" : "Marcel Grossmann", "declarer_email" : "marcel.grossmann@jacksay.com",
    "workpackage_code" : null, "activity_pfi" : "", "activity_uid" : null,
    "other_key" : "teaching",
    "date": "2022-01-14", "duration": 4.0,
    "comment": "Cours"
  }

,
  { "uid": "imported-ts-19",
    "declarer" : "Marcel Grossmann", "declarer_email" : "marcel.grossmann@jacksay.com",
    "workpackage_code" : null, "activity_pfi" : "", "activity_uid" : null,
    "other_key" : "research",
    "date": "2022-01-10", "duration": 6.0,
    "comment": "Entretien"
  },
  { "uid": "imported-ts-20",
    "declarer" : "Marcel Grossmann", "declarer_email" : "marcel.grossmann@jacksay.com",
    "workpackage_code" : null, "activity_pfi" : "", "activity_uid" : null,
    "other_key" : "other",
    "date": "2022-01-11", "duration": 4.0,
    "comment": "Réunion de laboratoire"
  },
  { "uid": "imported-ts-21",
    "declarer" : "Marcel Grossmann", "declarer_email" : "marcel.grossmann@jacksay.com",
    "workpackage_code" : "WP2", "activity_pfi" : "", "activity_uid" : "A0003",
    "other_key" : null,
    "date": "2022-01-12", "duration": 6.0,
    "comment": ""
  },

  { "uid": "imported-ts-22",
    "declarer" : "Marcel Grossmann", "declarer_email" : "marcel.grossmann@jacksay.com",
    "workpackage_code" : "WP1", "activity_pfi" : "", "activity_uid" : "A0003",
    "other_key" : null,
    "date": "2022-01-14", "duration": 4.0,
    "comment": ""
  }

]
 No newline at end of file
+163 −0
Original line number Diff line number Diff line
<?php
/**
 * Created by PhpStorm.
 * User: bouvry
 * Date: 04/10/19
 * Time: 11:49
 */

namespace Oscar\Command;


use Moment\Moment;
use Oscar\Entity\Activity;
use Oscar\Entity\Authentification;
use Oscar\Entity\LogActivity;
use Oscar\Entity\Person;
use Oscar\Entity\Role;
use Oscar\Entity\TimeSheet;
use Oscar\Entity\WorkPackage;
use Oscar\Entity\WorkPackagePerson;
use Oscar\Formatter\TimesheetActivityPeriodFormatter;
use Oscar\Service\ConnectorService;
use Oscar\Service\OscarConfigurationService;
use Oscar\Service\OscarUserContext;
use Oscar\Service\PersonService;
use Oscar\Service\TimesheetService;
use Oscar\Utils\DateTimeUtils;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Zend\Validator\Date;

class OscarTimesheetImportCommand extends OscarAdvancedCommandAbstract
{
    protected static $defaultName = 'timesheets:import';

    const ARG_FILE = 'file';

    protected function configure()
    {
        $this
            ->setDescription("Importation de feuille de temps.")
            ->addArgument(self::ARG_FILE, InputArgument::REQUIRED)
        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output) :int
    {
        parent::execute($input, $output);

        /// OPTIONS and PARAMETERS
        $filepath = $input->getArgument(self::ARG_FILE);

        $timesheets = json_decode(file_get_contents($filepath), true);

        try {
            // Récupération des créneaux importés
            $alreadyImportedUid = [];
            foreach ($timesheets as $timesheetData) {
                $alreadyImportedUid[] = $timesheetData['uid'];
            }
            $importedTimesheets = $this->getTimesheetService()->getImportedTimesheetsByUid($alreadyImportedUid);

            // Configuration des hors-lots
            $othersConfig = $this->getTimesheetService()->getOthersWP();

            foreach ($timesheets as $timesheetData) {
                $tsUID = $timesheetData['uid'];
                if( array_key_exists($tsUID, $importedTimesheets) ){
                    $this->getIO()->writeln("<info>Entrée '$tsUID' déjà importée</info>");
                    continue;
                }

                // On distingue les créneaux de projet/hors-lot
                $tsActivityUid = $timesheetData['activity_uid'];
                $tsOtherCode = $timesheetData['other_key'];
                $tsWpCode = $timesheetData['workpackage_code'];

                if( $tsActivityUid == null && $tsWpCode == null && $tsOtherCode == null ){
                    $this->getIO()->error("L'entrée $tsUID est incomplète (référence Activité/Hors-lots non-définie)");
                    continue;
                }

                // --- Récupération du déclarant
                $tsDeclarerEmail = $timesheetData['declarer_email'];
                $declarer = $this->getPersonService()->getPersonByEmail($tsDeclarerEmail);

                $activity = null;
                $workpackage = null;
                $label = null;

                // --- Récupération de l'activité
                if( $tsActivityUid ){
                    $activity = $this->getProjectGrantService()->getActivityByImportedUid($tsActivityUid);

                    if( !$activity->hasDeclarant($declarer) ){
                        $this->getIO()->warning("Le créneau '$tsUID' ne peut être importer pour '$declarer', 
                        il n'est pas identifié comme déclarant sur l'activité");
                        continue;
                    }

                    $workpackage = $activity->getWorkpackageByCode($tsWpCode);
                    if (!$workpackage ){
                        $this->getIO()->warning("Impossible de charger le lot '$tsWpCode' dans '$activity'");
                        continue;
                    }
                    $label = (string)$workpackage;
                } else {
                    if( !array_key_exists($tsOtherCode, $othersConfig) ){
                        $this->getIO()->warning("Impossible le créneau '$tsOtherCode', 
                            ce type hors-lot est absent de la configuration");
                        continue;
                    }
                    $label = $tsOtherCode;
                }

                // --- Calibration de la durée du créneau
                $day = $timesheetData['date'];
                $duration = $timesheetData['duration'];
                $comment = $timesheetData['comment'];
                $from = new \DateTime("$day 08:00:00");
                $to = new \DateTime("$day 08:00:00");
                $hours = intval($duration);
                $minutes = intval(($duration-$hours)*60);
                $interval = \DateInterval::createFromDateString("$hours hours $minutes minutes");
                $to->add($interval);

                // --- Enregistrement
                $timesheet = new TimeSheet();
                $timesheet->setPerson($declarer)
                    ->setWorkpackage($workpackage)
                    ->setLabel($label)
                    ->setActivity($activity)
                    ->setComment($comment)
                    ->setDateFrom($from)
                    ->setDateTo($to)
                    ->setDateSync((new \DateTime()))
                    ->setIcsUid($tsUID);

                $this->getIO()->writeln("<success>Importation de $timesheet</success>");
                $this->getTimesheetService()->getEntityManager()->persist($timesheet);
                $this->getTimesheetService()->getEntityManager()->flush($timesheet);

            }
        } catch (\Exception $e) {
            $this->getIO()->error("Impossible d'importer les créneaux : " . $e->getMessage());
            return 1;
        }

        return 0;
    }

    /**
     * @return TimesheetService
     */
    protected function getTimesheetService(){
        return $this->getServicemanager()->get(TimesheetService::class);
    }
}
 No newline at end of file
Loading