Commit e3662704 authored by Stephane Bouvry's avatar Stephane Bouvry
Browse files

Gestion des modifications :

 - Modification de la date de fin
 - Ajout d'une personne/rôle
 - Suppression d'une personne/rôle
parent eb82d520
Loading
Loading
Loading
Loading
Loading
+16 −33
Original line number Diff line number Diff line
@@ -71,20 +71,32 @@ class ActivityAvenantsController extends AbstractOscarController implements UseL
                } catch (\Exception $e) {
                    return $this->jsonError("Impossible de charger les avenants");
                }
                break;

            case 'POST':
                // TODO Tester les droits d'accès
                $datas = $_POST;
                $action = $_POST['action'];
                switch($action){
                    case "create":
                        try {
                    $datas['file'] = $this->fileAvenantDrop($activity);
                            $datas['status'] = ActivityAvenant::STATUS_DRAFT;
                    $this->getLoggerService()->debug(print_r($datas, true));
                    $this->getActivityAvenantsService()->createAvenantFromArray($activity, $datas);
                            $this->getActivityAvenantsService()->saveAvenantFromArray($activity, $datas);
                            return $this->getResponseOk("Avenant ajouté");
                        } catch (\Exception $e) {
                            return $this->jsonError("Impossible d'ajouter l'avenant : " . $e->getMessage());
                        }
                    case "update":
                        try {
                            $this->getActivityAvenantsService()->saveAvenantFromArray($activity, $datas);
                            return $this->getResponseOk("Avenant modifié");

                        } catch (\Exception $e) {
                            return $this->jsonError("Impossible de mettre à jour l'avenant : " . $e->getMessage());
                        }
                    default:
                        return $this->jsonError("Action non reconnue");

                }

            case 'DELETE':
                // TODO Tester les droits d'accès
@@ -151,33 +163,4 @@ class ActivityAvenantsController extends AbstractOscarController implements UseL
            throw new OscarException("Impossible de charger l'activité $idActivity");
        }
    }

    private function fileAvenantDrop( Activity $activity ) :string
    {
        if( !array_key_exists('file', $_FILES) ){
            $this->getLoggerService()->error("Aucun fichier d'avenant envoyé");
            throw new OscarException("Fichier manquant");
        }
        try {
            $uploader = new FileUploadStandard();
            $avenant_directory = $this->getOscarConfigurationService()->getDocumentDropLocation();
            $filename_pattern = $this->getOscarConfigurationService()->getConfiguration('avenant_filename');
            $filename = sprintf(
                $filename_pattern,
                $activity->getId(),
                (new \DateTime())->format('Y-m-d'),
                uniqid()
            );
            $mimes = ["application/pdf" => "pdf"];
            $uploader->setDestination($avenant_directory)
                ->setFilename($filename)
                ->setMimesAllowed($mimes);
            $uploader->updoad($_FILES['file']);
            $this->getLoggerService()->info("Upload ok");
            return $uploader->getUploadName();
        } catch (\Exception $e){
            $this->getLoggerService()->error($e->getMessage());
            throw new OscarException("Impossible de téléverser le fichier de l'avenant : " . $e->getMessage());
        }
    }
}
+29 −0
Original line number Diff line number Diff line
@@ -3,6 +3,8 @@
namespace Oscar\Entity;

use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
@@ -36,6 +38,14 @@ class ActivityAvenant
     */
    private Activity $activity;

    /**
     * Liste des modifications
     *
     * @var ArrayCollection
     * @ORM\OneToMany(targetEntity="ActivityAvenantModification", mappedBy="avenant", cascade={"remove"})
     */
    protected $modifications;

    /**
     * @var string
     * @ORM\Column(type="string")
@@ -54,6 +64,11 @@ class ActivityAvenant
     */
    private int $status;

    public function __construct()
    {
        $this->modifications = new ArrayCollection();
    }

    /**
     * @return mixed
     */
@@ -122,6 +137,20 @@ class ActivityAvenant
        return $this;
    }

    /**
     * @return ActivityAvenantModification[]
     */
    public function getModifications()
    {
        return $this->modifications;
    }

    public function setModifications($modifications): self
    {
        $this->modifications = $modifications;
        return $this;
    }

    public function getResourceId()
    {
        return self::class;
+77 −30
Original line number Diff line number Diff line
@@ -10,6 +10,25 @@ use Doctrine\ORM\Mapping as ORM;
 */
class ActivityAvenantModification
{
    const TYPE_PERSON_DEL = 'personDel';
    const TYPE_PERSON_ADD = 'personAdd';
    const TYPE_ORGANIZATION_ADD = 'organizationAdd';
    const TYPE_ORGANIZATION_DEL = 'organizationDel';
    const TYPE_CHANGE_AMOUNT = 'changeAmount';
    const TYPE_DATE_END = 'dateEnd';

    public static function getTypes(): array
    {
        return array(
            self::TYPE_PERSON_DEL => "Suppression d'une personne",
            self::TYPE_PERSON_ADD => "Ajout d'une personne",
            self::TYPE_ORGANIZATION_ADD => "Suppression d'une organization",
            self::TYPE_ORGANIZATION_DEL => "Ajout d'une organisation",
            self::TYPE_CHANGE_AMOUNT => "Modification du montant",
            self::TYPE_DATE_END => "Modification de la date de fin",
        );
    }

    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
@@ -17,12 +36,6 @@ class ActivityAvenantModification
     */
    private $id;

    /**
     * @var Activity
     * @ORM\ManyToOne(targetEntity="ActivityAvenant")
     */
    private Activity $avenant;

    /**
     * @var string
     * @ORM\Column(type="string")
@@ -30,43 +43,55 @@ class ActivityAvenantModification
    private string $type;

    /**
     * @var string
     * @ORM\Column(type="string")
     * @var ?string
     * @ORM\Column(type="string", nullable=true)
     */
    private string $newValue1;
    private ?string $newValue1;

    /**
     * @var string
     * @ORM\Column(type="string")
     * @var ?string
     * @ORM\Column(type="string", nullable=true)
     */
    private string $oldValue1;
    private ?string $oldValue1;

    /**
     * @var string
     * @ORM\Column(type="string")
     * @var ?string
     * @ORM\Column(type="string", nullable=true)
     */
    private string $newValue2;
    private ?string $newValue2;

    /**
     * @var string
     * @ORM\Column(type="string")
     * @var ?string
     * @ORM\Column(type="string", nullable=true)
     */
    private string $oldValue2;
    private ?string $oldValue2;

    /**
     * @var ?string
     * @ORM\Column(type="string", nullable=true)
     */
    private ?string $info;

    /**
     * @var ActivityAvenant
     * @ORM\ManyToOne(targetEntity="ActivityAvenant", inversedBy="modifications")
     */
    private ActivityAvenant $avenant;

    /**
     * @return mixed
     */
    public function getId()
    public function getId(): mixed
    {
        return $this->id;
    }

    public function getAvenant(): Activity
    public function getAvenant(): ActivityAvenant
    {
        return $this->avenant;
    }

    public function setAvenant(Activity $avenant): self
    public function setAvenant(ActivityAvenant $avenant): self
    {
        $this->avenant = $avenant;
        return $this;
@@ -83,52 +108,74 @@ class ActivityAvenantModification
        return $this;
    }

    public function getNewValue1(): string
    public function getNewValue1(): ?string
    {
        return $this->newValue1;
    }

    public function setNewValue1(string $newValue1): self
    public function setNewValue1(?string $newValue1): self
    {
        $this->newValue1 = $newValue1;
        return $this;
    }

    public function getOldValue1(): string
    public function getOldValue1(): ?string
    {
        return $this->oldValue1;
    }

    public function setOldValue1(string $oldValue1): self
    public function setOldValue1(?string $oldValue1): self
    {
        $this->oldValue1 = $oldValue1;
        return $this;
    }

    public function getNewValue2(): string
    public function getNewValue2(): ?string
    {
        return $this->newValue2;
    }

    public function setNewValue2(string $newValue2): self
    public function setNewValue2(?string $newValue2): self
    {
        $this->newValue2 = $newValue2;
        return $this;
    }

    public function getOldValue2(): string
    public function getOldValue2(): ?string
    {
        return $this->oldValue2;
    }

    public function setOldValue2(string $oldValue2): self
    public function setOldValue2(?string $oldValue2): self
    {
        $this->oldValue2 = $oldValue2;
        return $this;
    }

    public function getResourceId()
    public function getInfo(): ?string
    {
        return $this->info;
    }

    public function setInfo(?string $info): self
    {
        $this->info = $info;
        return $this;
    }

    public function getResourceId(): string
    {
        return self::class;
    }

    public function toJson() :array {
        return [
            'id' => $this->getId(),
            'type' => $this->getType(),
            'value1' => $this->getNewValue1(),
            'value2' => $this->getNewValue2(),
            'oldValue1' => $this->getOldValue1(),
            'info' => $this->getInfo(),
        ];
    }
}
+176 −1
Original line number Diff line number Diff line
@@ -2,13 +2,20 @@

namespace Oscar\Service;

use Doctrine\ORM\Exception\NotSupported;
use Doctrine\ORM\Exception\ORMException;
use Moment\Moment;
use Oscar\Entity\Activity;
use Oscar\Entity\ActivityAvenant;
use Oscar\Entity\ActivityAvenantModification;
use Oscar\Entity\ActivityPayment;
use Oscar\Entity\Currency;
use Oscar\Entity\LogActivity;
use Oscar\Entity\Person;
use Oscar\Entity\Repository\ActivityPaymentRepository;
use Oscar\Entity\Role;
use Oscar\Exception\OscarException;
use Oscar\Strategy\Upload\FileUploadStandard;
use Oscar\Traits\UseActivityLogService;
use Oscar\Traits\UseActivityLogServiceTrait;
use Oscar\Traits\UseEntityManager;
@@ -71,6 +78,80 @@ class ActivityAvenantsService implements
        }
    }

    /**
     * @param Activity $activity
     * @param array $datas
     * @return void
     * @throws OscarException
     */
    public function saveAvenantFromArray(Activity $activity, array $datas): void
    {
        $this->getLoggerService()->debug(__METHOD__);
        $this->getLoggerService()->debug(json_encode($datas));

        if ($datas['id']) {
            $mode = "update";
            $avenant = $this->getEntityManager()->getRepository(ActivityAvenant::class)->find($datas['id']);
        }
        else {
            $mode = "create";
            $avenant = new ActivityAvenant();
            $avenant->setStatus(ActivityAvenant::STATUS_DRAFT);
            $avenant->setActivity($activity);
            $this->getEntityManager()->persist($avenant);
        }

        // Traitement des données
        try {
            $avenant_date = DateTimeUtils::getDateTimeFromStr($datas['dateAvenant']);
        } catch (\Exception $e) {
            throw new OscarException("Date de l'avenant invalide");
        }

        $idsModification = [];
        foreach ($avenant->getModifications() as $modification) {
            $idsModification[] = $modification->getId();
        }

        if ($datas['modifications']) {
            $modificationsSend = json_decode($datas['modifications'], true);
            foreach ($modificationsSend as $modification) {
                $idsModification = array_diff($idsModification, [$modification['id']]);
                $this->saveModification($avenant, $modification);
            }
        }

        $fileUploaded = $this->uploadAvenantFile($activity);
        if (($mode == "update" && $fileUploaded)) {
            $this->deleteAvenantFile($avenant->getFilename());
        }
        if ($fileUploaded) {
            $avenant->setFilename($fileUploaded);
        }

        if (($mode == "create" && !$fileUploaded)) {
            throw new OscarException("Fichier manquant");
        }

        // Modification
        foreach ($idsModification as $modificationId) {
            $modification = $this->getEntityManager()->getRepository(ActivityAvenantModification::class)->find($modificationId);
            $this->getEntityManager()->remove($modification);
        }

        try {
            $avenant->setDateAvenant($avenant_date);
            $avenant->setComment($datas['comment']);
            $this->getEntityManager()->flush();
        } catch (\Exception $e) {
            $this->getLoggerService()->critical($e->getMessage());
            if ($fileUploaded) {
                $this->deleteAvenantFile($fileUploaded);
            }
            throw new OscarException("Impossible de créer l'avenant");
        }
    }

    /**
     * @param mixed $id
     * @return void
@@ -84,13 +165,41 @@ class ActivityAvenantsService implements
            $this->deleteAvenantFile($avenant->getFilename());
            $this->getEntityManager()->remove($avenant);
            $this->getEntityManager()->flush();

        } catch (\Exception $e) {
            $this->getLoggerService()->critical($e->getMessage());
            throw new OscarException("Impossible de supprimer l'avenant");
        }
    }

    private function uploadAvenantFile(Activity $activity): ?string
    {
        if (!array_key_exists('file', $_FILES)) {
            $this->getLoggerService()->error("Aucun fichier d'avenant envoyé");
            return null;
        }
        try {
            $uploader = new FileUploadStandard();
            $avenant_directory = $this->getOscarConfigurationService()->getDocumentDropLocation();
            $filename_pattern = $this->getOscarConfigurationService()->getConfiguration('avenant_filename');
            $filename = sprintf(
                $filename_pattern,
                $activity->getId(),
                (new \DateTime())->format('Y-m-d'),
                uniqid()
            );
            $mimes = ["application/pdf" => "pdf"];
            $uploader->setDestination($avenant_directory)
                ->setFilename($filename)
                ->setMimesAllowed($mimes);
            $uploader->updoad($_FILES['file']);
            $this->getLoggerService()->info("Upload ok");
            return $uploader->getUploadName();
        } catch (\Exception $e) {
            $this->getLoggerService()->error($e->getMessage());
            throw new OscarException("Impossible de téléverser le fichier de l'avenant : " . $e->getMessage());
        }
    }

    /**
     * @param string $filename
     * @return bool
@@ -124,6 +233,8 @@ class ActivityAvenantsService implements

        $size = filesize($location);
        $filename = $avenant->getFilename();

        // Déplacer le type dans la BDD
        $type = 'application/pdf';
        $content = FileSystemUtils::getInstance()->file_get_contents($location);

@@ -159,4 +270,68 @@ class ActivityAvenantsService implements
            throw new OscarException($msg);
        }
    }

    /**
     * @param ActivityAvenant $avenant
     * @param array $modification
     * @param bool $fetch
     * @return void
     * @throws NotSupported
     * @throws ORMException
     * @throws OscarException
     */
    private function saveModification(ActivityAvenant $avenant, array $modification, bool $fetch = false): void
    {
        $this->getLoggerService()->debug("Traitement modification");
        $this->getLoggerService()->debug(json_encode($modification));

        $type = $modification['type'];
        $newValue1 = $modification['value1'];
        $newValue2 = $modification['value2'];
        $oldValue1 = null;
        $oldValue2 = null;

        if( !array_key_exists($type, ActivityAvenantModification::getTypes()) ) {
            throw new OscarException("Le type d'avenant '$type' n'existe pas");
        }

        if ($modification['id']) {
            $avenantModif = $this->getEntityManager()->getRepository(ActivityAvenantModification::class)->find(
                $modification['id']
            );
        }
        else {
            $avenantModif = new ActivityAvenantModification();
            $this->getEntityManager()->persist($avenantModif);
            $avenantModif->setAvenant($avenant);
        }

        $msg = "Modification non-traitée";
        switch ($type) {
            case ActivityAvenantModification::TYPE_DATE_END:
                $moment = Moment::fromDateTime(new \DateTime($newValue1));
                $msg = "Modification de la date de fin pour " . $moment->format('d/m/Y');
                $oldValue1 = $avenant->getActivity()->getDateEndStr();
                break;
            case ActivityAvenantModification::TYPE_PERSON_DEL:
                $person = $this->getEntityManager()->getRepository(Person::class)->find($newValue1);
                $role = $this->getEntityManager()->getRepository(Role::class)->find($newValue2);
                $msg = "Suppression de $person ($role)";
                break;
            case ActivityAvenantModification::TYPE_PERSON_ADD:
                $person = $this->getEntityManager()->getRepository(Person::class)->find($newValue1);
                $role = $this->getEntityManager()->getRepository(Role::class)->find($newValue2);
                $msg = "Ajout de $person ($role)";
                break;
        }

        $avenantModif->setType($modification['type']);
        $avenantModif->setNewValue1($newValue1);
        $avenantModif->setNewValue2($newValue2);
        $avenantModif->setOldValue1($oldValue1);
        $avenantModif->setOldValue2($oldValue2);
        $avenantModif->setInfo($msg);

        $this->getLoggerService()->debug("MODIF : " . $modification['type']);
    }
}
 No newline at end of file
+27 −8
Original line number Diff line number Diff line
@@ -171,7 +171,6 @@ class ProjectGrantApiService implements UseEntityManager, UsePersonService, UseO
        OscarUserContext $oscarUserContext,
        array $perimeters
    ): array {

        $locked = $activity->isLocked();
        $editable = $oscarUserContext->hasPrivileges(Privileges::ACTIVITY_EDIT, $activity);
        $lockEditable = $oscarUserContext->hasPrivileges(Privileges::ACTIVITY_EDIT_LOCKED, $activity);
@@ -283,15 +282,27 @@ class ProjectGrantApiService implements UseEntityManager, UsePersonService, UseO

                case 'milestones':
                    $credentials['milestones'] = [
                        'read' => $oscarUserContext->hasPrivileges(Privileges::ACTIVITY_MILESTONE_SHOW, $activity),
                        'edit' => $oscarUserContext->hasPrivileges(Privileges::ACTIVITY_MILESTONE_MANAGE, $activity),
                        'progression' => $oscarUserContext->hasPrivileges(Privileges::ACTIVITY_MILESTONE_PROGRESSION, $activity),
                        'read'        => $oscarUserContext->hasPrivileges(
                            Privileges::ACTIVITY_MILESTONE_SHOW,
                            $activity
                        ),
                        'edit'        => $oscarUserContext->hasPrivileges(
                            Privileges::ACTIVITY_MILESTONE_MANAGE,
                            $activity
                        ),
                        'progression' => $oscarUserContext->hasPrivileges(
                            Privileges::ACTIVITY_MILESTONE_PROGRESSION,
                            $activity
                        ),
                    ];
                    break;

                case 'organizations':
                    $credentials['organizations'] = [
                        'edit' => !$locked && $oscarUserContext->hasPrivileges(Privileges::ACTIVITY_ORGANIZATION_MANAGE, $activity),
                        'edit' => !$locked && $oscarUserContext->hasPrivileges(
                                Privileges::ACTIVITY_ORGANIZATION_MANAGE,
                                $activity
                            ),
                        'read' => $oscarUserContext->hasPrivileges(Privileges::ACTIVITY_ORGANIZATION_SHOW, $activity),
                        'show' => $oscarUserContext->hasPrivileges(Privileges::ORGANIZATION_SHOW),
                    ];
@@ -305,7 +316,10 @@ class ProjectGrantApiService implements UseEntityManager, UsePersonService, UseO
                    break;
                case 'persons':
                    $credentials['persons'] = [
                        'edit' => !$locked && $oscarUserContext->hasPrivileges(Privileges::ACTIVITY_PERSON_MANAGE, $activity),
                        'edit' => !$locked && $oscarUserContext->hasPrivileges(
                                Privileges::ACTIVITY_PERSON_MANAGE,
                                $activity
                            ),
                        'read' => $oscarUserContext->hasPrivileges(Privileges::ACTIVITY_PERSON_SHOW, $activity),
                        'show' => $oscarUserContext->hasPrivileges(Privileges::PERSON_SHOW),
                    ];
@@ -354,7 +368,6 @@ class ProjectGrantApiService implements UseEntityManager, UsePersonService, UseO
        ?Url $urlPlugin = null,
        ?array $perimeters = null
    ): array {

        $datas = [
            "api" => "Oscar Activity API"
        ];
@@ -1205,8 +1218,8 @@ class ProjectGrantApiService implements UseEntityManager, UsePersonService, UseO
    }

    /**
     * @throws NotSupported
     * @return array
     * @throws NotSupported
     */
    private function getCurrencies(): array
    {
@@ -1240,6 +1253,11 @@ class ProjectGrantApiService implements UseEntityManager, UsePersonService, UseO
        $out = [];
        /** @var ActivityAvenant $item */
        foreach ($result as $item) {
            $modifications = [];
            foreach ($item->getModifications() as $modification) {
                $modifications[] = $modification->toJson();
            }

            $out[] = [
                'id'           => $item->getId(),
                'comment'      => $item->getComment(),
@@ -1247,6 +1265,7 @@ class ProjectGrantApiService implements UseEntityManager, UsePersonService, UseO
                'date'         => $item->getDateAvenant()->format('Y-m-d'),
                'status'       => $item->getStatus(),
                'status_text'  => $item->getStatusLabel(),
                'modifications'      => $modifications,
                'url_api'      => $urlPlugin->fromRoute('avenant/api', [
                    'activity_id' => $activity->getId(),
                    'avenant_id'  => $item->getId()
Loading