Преглед изворни кода

Merge branch 'develop' of gitlab.2iopenservice.com:opentalent/api into develop

Vincent GUFFON пре 4 година
родитељ
комит
bb8f4af4e3

+ 10 - 1
.env

@@ -41,4 +41,13 @@ JWT_PASSPHRASE=opentalent
 
 ###> opentalent config folder ###
 OPENTALENT_CONFIG=/config/opentalent
-###< opentalent config folder ###
+###< opentalent config folder ###
+
+###> dolibarr client ###
+DOLIBARR_API_BASE_URI='https://prod-erp.2iopenservice.com/api/index.php/'
+DOLIBARR_API_TOKEN='Bocc4zC0J186v8J6QCqu7DnoIw4I7mCJ'
+###< dolibarr client ###
+
+###> mobyt client ###
+MOBYT_API_BASE_URI='https://app.mobyt.fr/API/v1.0/REST/'
+###< mobyt client ###

+ 2 - 2
composer.json

@@ -8,13 +8,12 @@
         }
     ],
     "require": {
-        "php": ">=7.2.5",
+        "php": ">=8.0",
         "ext-ctype": "*",
         "ext-iconv": "*",
         "api-platform/core": "^2.6",
         "blackfire/php-sdk": "^1.23",
         "composer/package-versions-deprecated": "^1.11",
-
         "doctrine/doctrine-bundle": "^2.1",
         "doctrine/doctrine-migrations-bundle": "^3.0",
         "doctrine/orm": "^2.9",
@@ -44,6 +43,7 @@
         "webonyx/graphql-php": "^14.3"
     },
     "require-dev": {
+        "cyclonedx/cyclonedx-php-composer": "^3.4",
         "symfony/maker-bundle": "^1.21",
         "symfony/phpunit-bridge": "^5.3",
         "symfony/stopwatch": "^5.3",

+ 277 - 2
composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "f79e9967cd9dbb3f34a7d034ed1eb7cf",
+    "content-hash": "a5c7e49963eecc92446ad58b8f5a4754",
     "packages": [
         {
             "name": "api-platform/core",
@@ -7747,6 +7747,89 @@
         }
     ],
     "packages-dev": [
+        {
+            "name": "cyclonedx/cyclonedx-php-composer",
+            "version": "v3.4.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/CycloneDX/cyclonedx-php-composer.git",
+                "reference": "d3dc6433f9b9330de8e5ba229450db4d2400bb05"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/CycloneDX/cyclonedx-php-composer/zipball/d3dc6433f9b9330de8e5ba229450db4d2400bb05",
+                "reference": "d3dc6433f9b9330de8e5ba229450db4d2400bb05",
+                "shasum": ""
+            },
+            "require": {
+                "composer-plugin-api": "^2.0",
+                "ext-dom": "*",
+                "ext-filter": "*",
+                "ext-json": "*",
+                "ext-libxml": "*",
+                "package-url/packageurl-php": "^1.0",
+                "php": "^7.3 || ^8.0",
+                "swaggest/json-schema": "^0.12.35"
+            },
+            "require-dev": {
+                "composer/composer": "^2.0.13",
+                "ext-simplexml": "*",
+                "phpunit/phpunit": "9.5.9",
+                "roave/security-advisories": "dev-latest"
+            },
+            "type": "composer-plugin",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.x-dev"
+                },
+                "class": "CycloneDX\\Composer\\Plugin",
+                "composer-normalize": {
+                    "indent-size": 4,
+                    "indent-style": "space"
+                },
+                "unused": [
+                    "composer-plugin-api"
+                ]
+            },
+            "autoload": {
+                "psr-4": {
+                    "CycloneDX\\Composer\\": "src/Composer/",
+                    "CycloneDX\\Core\\": "src/Core/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "nscuro",
+                    "email": "nscuro@protonmail.com",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Jan Kowalleck",
+                    "email": "jan.kowalleck@gmail.com",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Creates CycloneDX Software Bill-of-Materials (SBOM) from PHP Composer projects",
+            "homepage": "https://github.com/CycloneDX/cyclonedx-php-composer/",
+            "keywords": [
+                "CycloneDX",
+                "SBOM",
+                "bill-of-materials",
+                "bom",
+                "package-url",
+                "purl",
+                "software-bill-of-materials"
+            ],
+            "support": {
+                "issues": "https://github.com/CycloneDX/cyclonedx-php-composer/issues",
+                "source": "https://github.com/CycloneDX/cyclonedx-php-composer/tree/v3.4.1"
+            },
+            "time": "2021-09-16T13:53:03+00:00"
+        },
         {
             "name": "nikic/php-parser",
             "version": "v4.13.0",
@@ -7803,6 +7886,198 @@
             },
             "time": "2021-09-20T12:20:58+00:00"
         },
+        {
+            "name": "package-url/packageurl-php",
+            "version": "1.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/package-url/packageurl-php.git",
+                "reference": "3d593b87966d3dff60d6cc02fd0a83fccb0a01f4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/package-url/packageurl-php/zipball/3d593b87966d3dff60d6cc02fd0a83fccb0a01f4",
+                "reference": "3d593b87966d3dff60d6cc02fd0a83fccb0a01f4",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.3 || ^8.0"
+            },
+            "require-dev": {
+                "ext-json": "*",
+                "phpunit/phpunit": "9.5.4",
+                "roave/security-advisories": "dev-latest"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "PackageUrl\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jan Kowalleck",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A parser and builder based on the package url spec.",
+            "homepage": "https://github.com/package-url/packageurl-php",
+            "keywords": [
+                "package",
+                "package-url",
+                "packageurl",
+                "purl",
+                "url"
+            ],
+            "support": {
+                "issues": "https://github.com/package-url/packageurl-php/issues",
+                "source": "https://github.com/package-url/packageurl-php/tree/1.0.3"
+            },
+            "time": "2021-05-13T17:29:18+00:00"
+        },
+        {
+            "name": "phplang/scope-exit",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phplang/scope-exit.git",
+                "reference": "239b73abe89f9414aa85a7ca075ec9445629192b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phplang/scope-exit/zipball/239b73abe89f9414aa85a7ca075ec9445629192b",
+                "reference": "239b73abe89f9414aa85a7ca075ec9445629192b",
+                "shasum": ""
+            },
+            "require-dev": {
+                "phpunit/phpunit": "*"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "PhpLang\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD"
+            ],
+            "authors": [
+                {
+                    "name": "Sara Golemon",
+                    "email": "pollita@php.net",
+                    "homepage": "https://twitter.com/SaraMG",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Emulation of SCOPE_EXIT construct from C++",
+            "homepage": "https://github.com/phplang/scope-exit",
+            "keywords": [
+                "cleanup",
+                "exit",
+                "scope"
+            ],
+            "support": {
+                "issues": "https://github.com/phplang/scope-exit/issues",
+                "source": "https://github.com/phplang/scope-exit/tree/master"
+            },
+            "time": "2016-09-17T00:15:18+00:00"
+        },
+        {
+            "name": "swaggest/json-diff",
+            "version": "v3.8.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/swaggest/json-diff.git",
+                "reference": "bb3e3b4e9d842bb2e48f31ea568d0459968d1d42"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/swaggest/json-diff/zipball/bb3e3b4e9d842bb2e48f31ea568d0459968d1d42",
+                "reference": "bb3e3b4e9d842bb2e48f31ea568d0459968d1d42",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.23"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Swaggest\\JsonDiff\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Viacheslav Poturaev",
+                    "email": "vearutop@gmail.com"
+                }
+            ],
+            "description": "JSON diff/rearrange/patch/pointer library for PHP",
+            "support": {
+                "issues": "https://github.com/swaggest/json-diff/issues",
+                "source": "https://github.com/swaggest/json-diff/tree/v3.8.3"
+            },
+            "time": "2021-09-25T22:09:03+00:00"
+        },
+        {
+            "name": "swaggest/json-schema",
+            "version": "v0.12.38",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/swaggest/php-json-schema.git",
+                "reference": "ab7cca149a5822acc5be538dd6c008a348f6cb89"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/swaggest/php-json-schema/zipball/ab7cca149a5822acc5be538dd6c008a348f6cb89",
+                "reference": "ab7cca149a5822acc5be538dd6c008a348f6cb89",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "ext-mbstring": "*",
+                "php": ">=5.4",
+                "phplang/scope-exit": "^1.0",
+                "swaggest/json-diff": "^3.8.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.23"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Swaggest\\JsonSchema\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Viacheslav Poturaev",
+                    "email": "vearutop@gmail.com"
+                }
+            ],
+            "description": "High definition PHP structures with JSON-schema based validation",
+            "support": {
+                "email": "vearutop@gmail.com",
+                "issues": "https://github.com/swaggest/php-json-schema/issues",
+                "source": "https://github.com/swaggest/php-json-schema/tree/v0.12.38"
+            },
+            "time": "2021-09-17T13:54:01+00:00"
+        },
         {
             "name": "symfony/maker-bundle",
             "version": "v1.33.0",
@@ -8060,7 +8335,7 @@
     "prefer-stable": false,
     "prefer-lowest": false,
     "platform": {
-        "php": ">=7.2.5",
+        "php": ">=8.0",
         "ext-ctype": "*",
         "ext-iconv": "*"
     },


+ 9 - 0
config/packages/framework.yaml

@@ -22,3 +22,12 @@ framework:
                 base_uri: 'https://entreprise.data.gouv.fr/api/sirene/v3/etablissements/'
             openstreetmap:
                 base_uri: 'https://nominatim.openstreetmap.org/'
+            dolibarr_client:
+                base_uri: '%env(DOLIBARR_API_BASE_URI)%'
+                headers:
+                    DOLAPIKEY: '%env(DOLIBARR_API_TOKEN)%'
+                    Accept: 'application/json'
+            mobyt_client:
+                base_uri: '%env(MOBYT_API_BASE_URI)%'
+                headers:
+                    Content-Type: 'application/json'


+ 133 - 0
src/ApiResources/Dolibarr/DolibarrAccount.php

@@ -0,0 +1,133 @@
+<?php
+declare(strict_types=1);
+
+namespace App\ApiResources\Dolibarr;
+
+use ApiPlatform\Core\Annotation\ApiProperty;
+use ApiPlatform\Core\Annotation\ApiResource;
+use Symfony\Component\Serializer\Annotation\Groups;
+
+/**
+ * Données de l'organization retournées par l'API Dolibarr
+ * (aussi nommé 'ThirdParty' ou 'Society' dans dolibarr)
+ */
+#[ApiResource(
+    itemOperations: [
+        'get' => [
+            'security' => '(is_granted("ROLE_ADMIN_CORE") or 
+                            is_granted("ROLE_ADMINISTRATIF_MANAGER_CORE") or 
+                            is_granted("ROLE_PEDAGOGICS_MANAGER_CORE") or 
+                            is_granted("ROLE_FINANCIAL_MANAGER_CORE")
+                           ) and object.getOrganizationId() == user.getOrganization().getId()',
+            'method' => 'GET',
+            'path' => '/dolibarr/account/{organizationId}',
+            'requirements' => ['organizationId' => '\d+'],
+            'normalization_context' => [
+                'groups' => ['dolibarr_get']
+            ],
+        ],
+    ],
+    compositeIdentifier: false,
+)]
+class DolibarrAccount
+{
+    #[ApiProperty(identifier: true)]
+    #[Groups('dolibarr_get')]
+    private int $organizationId;
+
+    /**
+     * Dolibarr societies pk
+     */
+    #[Groups('dolibarr_get')]
+    private int $socId;
+
+    /**
+     * Opentalent client ref
+     */
+    #[Groups('dolibarr_get')]
+    private string $clientNumber = "";
+
+    /**
+     * Opentalent product owned
+     */
+    #[Groups('dolibarr_get')]
+    private string $product = "";
+
+    /**
+     * Contract and services currently active
+     */
+    #[Groups('dolibarr_get')]
+    private ?object $contract = null;
+
+    /**
+     * Last bills
+     */
+    #[Groups('dolibarr_get')]
+    private array $bills = [];
+
+    public function getOrganizationId(): int
+    {
+        return $this->organizationId;
+    }
+
+    public function setOrganizationId(int $organizationId): void
+    {
+        $this->organizationId = $organizationId;
+    }
+
+    public function getSocId(): int
+    {
+        return $this->socId;
+    }
+
+    public function setSocId(int $socId): void
+    {
+        $this->socId = $socId;
+    }
+
+    public function getClientNumber(): string
+    {
+        return $this->clientNumber;
+    }
+
+    public function setClientNumber(string $clientNumber): void
+    {
+        $this->clientNumber = $clientNumber;
+    }
+
+    public function getProduct(): string
+    {
+        return $this->product;
+    }
+
+    public function setProduct(string $product): void
+    {
+        $this->product = $product;
+    }
+
+    public function getContract(): ?object
+    {
+        return $this->contract;
+    }
+
+    public function setContract(?object $contract): void
+    {
+        $this->contract = $contract;
+    }
+
+    public function getBills(): array
+    {
+        return $this->bills;
+    }
+
+    public function setBills(array $bills): void
+    {
+        $this->bills = $bills;
+    }
+
+    public function addBill(DolibarrBill $bill): void
+    {
+        $this->bills[] = $bill;
+    }
+
+}

+ 152 - 0
src/ApiResources/Dolibarr/DolibarrBill.php

@@ -0,0 +1,152 @@
+<?php
+declare(strict_types=1);
+
+namespace App\ApiResources\Dolibarr;
+
+use ApiPlatform\Core\Annotation\ApiProperty;
+use ApiPlatform\Core\Annotation\ApiResource;
+use Symfony\Component\Serializer\Annotation\Groups;
+
+/**
+ * Bill of a society, retrieved from dolibarr
+ */
+#[ApiResource(
+    collectionOperations: [
+        'get' => [
+            'method' => 'GET',
+            'path' => '/dolibarr/bills/{socId}',
+            'requirements' => ['socId' => '\d+'],
+            'normalization_context' => [
+                'groups' => ['dolibarr_get']
+            ]
+        ],
+    ]
+)]
+class DolibarrBill
+{
+    /**
+     * Id of the dolibarr bill ( = invoice)
+     */
+    #[ApiProperty(identifier: true)]
+    #[Groups('dolibarr_get')]
+    private int $id;
+
+    /**
+     * Bill reference
+     */
+    #[Groups('dolibarr_get')]
+    private string $ref;
+
+    /**
+     * Id of the society
+     */
+    #[Groups('dolibarr_get')]
+    private int $socId;
+
+    /**
+     * Date of the bill
+     */
+    #[Groups('dolibarr_get')]
+    private \DateTime $date;
+
+    /**
+     * Amount (tax excluded)
+     */
+    #[Groups('dolibarr_get')]
+    private float $taxExcludedAmount;
+
+    /**
+     * Amount (tax included)
+     */
+    #[Groups('dolibarr_get')]
+    private float $taxIncludedAmount;
+
+    /**
+     * Is the bill paid or not
+     */
+    #[Groups('dolibarr_get')]
+    private bool $paid;
+
+    public function getId(): int
+    {
+        return $this->id;
+    }
+
+    public function setId(int $id): void
+    {
+        $this->id = $id;
+    }
+
+    public function getRef(): string
+    {
+        return $this->ref;
+    }
+
+    public function setRef(string $ref): void
+    {
+        $this->ref = $ref;
+    }
+
+    public function getSocId(): int
+    {
+        return $this->socId;
+    }
+
+    public function setSocId(int $socId): void
+    {
+        $this->socId = $socId;
+    }
+
+    public function getDate(): \DateTime
+    {
+        return $this->date;
+    }
+
+    public function setDate(\DateTime $date): void
+    {
+        $this->date = $date;
+    }
+
+    /**
+     * @return float
+     */
+    public function getTaxExcludedAmount(): float
+    {
+        return $this->taxExcludedAmount;
+    }
+
+    /**
+     * @param float $taxExcludedAmount
+     */
+    public function setTaxExcludedAmount(float $taxExcludedAmount): void
+    {
+        $this->taxExcludedAmount = $taxExcludedAmount;
+    }
+
+    /**
+     * @return float
+     */
+    public function getTaxIncludedAmount(): float
+    {
+        return $this->taxIncludedAmount;
+    }
+
+    /**
+     * @param float $taxIncludedAmount
+     */
+    public function setTaxIncludedAmount(float $taxIncludedAmount): void
+    {
+        $this->taxIncludedAmount = $taxIncludedAmount;
+    }
+
+    public function getPaid(): bool
+    {
+        return $this->paid;
+    }
+
+    public function setPaid(bool $paid): void
+    {
+        $this->paid = $paid;
+    }
+
+}

+ 80 - 0
src/ApiResources/Dolibarr/DolibarrContract.php

@@ -0,0 +1,80 @@
+<?php
+declare(strict_types=1);
+
+namespace App\ApiResources\Dolibarr;
+
+use ApiPlatform\Core\Annotation\ApiProperty;
+use ApiPlatform\Core\Annotation\ApiResource;
+use Symfony\Component\Serializer\Annotation\Groups;
+
+/**
+ * Contract of a society, retrieved from dolibarr
+ */
+#[ApiResource(
+    itemOperations: [
+        'get' => [
+            'method' => 'GET',
+            'path' => '/dolibarr/contract/{ref}',
+            'requirements' => ['socId' => '\d+'],
+            'normalization_context' => [
+                'groups' => ['dolibarr_get']
+            ]
+        ],
+    ]
+)]
+class DolibarrContract
+{
+    /**
+     * Reference of the dolibarr contract
+     */
+    #[ApiProperty(identifier: true)]
+    #[Groups('dolibarr_get')]
+    private string $ref;
+
+    /**
+     * Id of the society
+     */
+    #[Groups('dolibarr_get')]
+    private int $socId;
+
+    /**
+     * Lines (services) included in the current contract
+     */
+    #[Groups('dolibarr_get')]
+    private array $lines = [];
+
+    public function getRef(): string
+    {
+        return $this->ref;
+    }
+
+    public function setRef(string $ref): void
+    {
+        $this->ref = $ref;
+    }
+
+    public function getSocId(): int
+    {
+        return $this->socId;
+    }
+
+    public function setSocId(int $socId): void
+    {
+        $this->socId = $socId;
+    }
+
+    public function getLines(): array
+    {
+        return $this->lines;
+    }
+
+    public function setLines(array $lines): void
+    {
+        $this->lines = $lines;
+    }
+
+    public function addLine(object $line): void
+    {
+        $this->lines[] = $line;
+    }
+}

+ 120 - 0
src/ApiResources/Dolibarr/DolibarrContractLine.php

@@ -0,0 +1,120 @@
+<?php
+declare(strict_types=1);
+
+namespace App\ApiResources\Dolibarr;
+
+use ApiPlatform\Core\Annotation\ApiProperty;
+use ApiPlatform\Core\Annotation\ApiResource;
+use Symfony\Component\Serializer\Annotation\Groups;
+
+/**
+ *  Lines (services) included in a society contract, as retrieved from dolibarr
+ */
+#[ApiResource(
+    collectionOperations: [
+        'get' => [
+            'method' => 'GET',
+            'path' => '/dolibarr/contract-lines/{contractId}',
+            'requirements' => ['contractId' => '\d+'],
+            'normalization_context' => [
+                'groups' => ['dolibarr_get']
+            ]
+        ],
+    ]
+)]
+class DolibarrContractLine
+{
+    #[ApiProperty(identifier: true)]
+    #[Groups('dolibarr_get')]
+    private int $id;
+
+    /**
+     * Id of the contract's line
+     */
+    #[Groups('dolibarr_get')]
+    private int $contractId;
+
+    /**
+     * Reference of the contracted service ( = product's reference)
+     */
+    #[Groups('dolibarr_get')]
+    private string $serviceRef;
+
+    /**
+     * Label of the contracted service
+     */
+    #[Groups('dolibarr_get')]
+    private string $serviceLabel;
+
+    /**
+     * Service active from date ...
+     */
+    #[Groups('dolibarr_get')]
+    private \DateTime $dateStart;
+
+    /**
+     * Service active to date ...
+     */
+    #[Groups('dolibarr_get')]
+    private \DateTime $dateEnd;
+
+    public function getId(): int
+    {
+        return $this->id;
+    }
+
+    public function setId(int $id): void
+    {
+        $this->id = $id;
+    }
+
+    public function getContractId(): int
+    {
+        return $this->contractId;
+    }
+
+    public function setContractId(int $contractId): void
+    {
+        $this->contractId = $contractId;
+    }
+
+    public function getServiceRef(): string
+    {
+        return $this->serviceRef;
+    }
+
+    public function setServiceRef(string $serviceRef): void
+    {
+        $this->serviceRef = $serviceRef;
+    }
+
+    public function getServiceLabel(): string
+    {
+        return $this->serviceLabel;
+    }
+
+    public function setServiceLabel(string $serviceLabel): void
+    {
+        $this->serviceLabel = $serviceLabel;
+    }
+
+    public function getDateStart(): \DateTime
+    {
+        return $this->dateStart;
+    }
+
+    public function setDateStart(\DateTime $dateStart): void
+    {
+        $this->dateStart = $dateStart;
+    }
+
+    public function isDateEnd(): \DateTime
+    {
+        return $this->dateEnd;
+    }
+
+    public function setDateEnd(\DateTime $dateEnd): void
+    {
+        $this->dateEnd = $dateEnd;
+    }
+}

+ 89 - 0
src/ApiResources/Mobyt/MobytUserStatus.php

@@ -0,0 +1,89 @@
+<?php
+declare(strict_types=1);
+
+namespace App\ApiResources\Mobyt;
+
+use ApiPlatform\Core\Annotation\ApiProperty;
+use ApiPlatform\Core\Annotation\ApiResource;
+use Symfony\Component\Serializer\Annotation\Groups;
+
+/**
+ * Statut de l'utilisateur Mobyt correspondant à l'organization donnée en paramètre
+ */
+#[ApiResource(
+    itemOperations: [
+        'get' => [
+            'security' => 'is_granted("ROLE_TEXTO") and object.getOrganizationId() == user.getOrganization().getId()',
+            'method' => 'GET',
+            'path' => '/mobyt/status/{organizationId}',
+            'requirements' => ['organizationId' => '\d+'],
+            'normalization_context' => [
+                'groups' => ['mobyt_get']
+            ],
+        ],
+    ]
+)]
+class MobytUserStatus
+{
+    #[ApiProperty(identifier: true)]
+    #[Groups('mobyt_get')]
+    private int $organizationId;
+
+    /**
+     * Is there a Mobyt account active for this user
+     */
+    #[Groups('mobyt_get')]
+    private bool $active = false;
+
+    /**
+     * Amount of sms remaining
+     */
+    #[Groups('mobyt_get')]
+    private int $amount = 0;
+
+    /**
+     * Money remaining
+     */
+    #[Groups('mobyt_get')]
+    private float $money = 0;
+
+    public function getOrganizationId(): int
+    {
+        return $this->organizationId;
+    }
+
+    public function setOrganizationId(int $organizationId): void
+    {
+        $this->organizationId = $organizationId;
+    }
+
+    public function isActive(): bool
+    {
+        return $this->active;
+    }
+
+    public function setActive(bool $active): void
+    {
+        $this->active = $active;
+    }
+
+    public function getAmount(): int
+    {
+        return $this->amount;
+    }
+
+    public function setAmount(int $amount): void
+    {
+        $this->amount = $amount;
+    }
+
+    public function getMoney(): float
+    {
+        return $this->money;
+    }
+
+    public function setMoney(float $money): void
+    {
+        $this->money = $money;
+    }
+}

+ 93 - 0
src/DataProvider/Dolibarr/DolibarrAccountDataProvider.php

@@ -0,0 +1,93 @@
+<?php
+declare(strict_types=1);
+
+namespace App\DataProvider\Dolibarr;
+
+use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
+use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
+use App\ApiResources\Dolibarr\DolibarrAccount;
+use App\ApiResources\Dolibarr\DolibarrBill;
+use App\ApiResources\Dolibarr\DolibarrContract;
+use App\ApiResources\Dolibarr\DolibarrContractLine;
+use App\Service\Dolibarr\DolibarrService;
+
+/**
+ * Custom provider pour les DolibarrAccounts récupérés via l'api dolibarr
+ * @package App\DataProvider\Utils
+ */
+final class DolibarrAccountDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface
+{
+    const PRODUCT_MAPPING = [
+        1 => 'PRODUCT_ARTIST',   # OT Artist
+        2 => 'PRODUCT_ARTIST_PREMIUM',   # OT Artist Premium
+        3 => 'PRODUCT_SCHOOL',   # OT School Standard
+        4 => 'PRODUCT_SCHOOL_PREMIUM',   # OT School Premium
+        5 => 'PRODUCT_MANAGER',   # OT Manager
+    ];
+    private DolibarrService $dolibarrService;
+
+    public function __construct(
+        DolibarrService $dolibarrService,
+    )
+    {
+        $this->dolibarrService = $dolibarrService;
+    }
+
+    public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
+    {
+        return DolibarrAccount::class === $resourceClass;
+    }
+
+    public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): ?DolibarrAccount
+    {
+        $dolibarrAccount = new DolibarrAccount();
+
+        $accountData = $this->dolibarrService->getSociety($id);
+
+        $dolibarrAccount->setOrganizationId($id);
+        $dolibarrAccount->setSocId((int)$accountData['id']);
+        $dolibarrAccount->setClientNumber($accountData['code_client']);
+        if ($accountData['array_options']['options_2iopen_software_used']) {
+            $dolibarrAccount->setProduct(
+                self::PRODUCT_MAPPING[(int)$accountData['array_options']['options_2iopen_software_used']]
+            );
+        }
+
+        // Get active contract and services
+        $contractData = $this->dolibarrService->getActiveContract($dolibarrAccount->getSocId());
+        if ($contractData !== null) {
+            $contract = new DolibarrContract();
+            $contract->setRef($contractData['ref']);
+            $contract->setSocId((int)$contractData['socid']);
+
+            foreach ($contractData['lines'] as $lineData) {
+                $line = new DolibarrContractLine();
+                $line->setId((int)$lineData['id']);
+                $line->setContractId((int)$lineData['fk_contrat']);
+                $line->setServiceRef($lineData['product_ref']);
+                $line->setServiceLabel($lineData['product_label']);
+                $line->setDateStart(new \DateTime(date('c', $lineData['date_start'])));
+                $line->setDateEnd(new \DateTime(date('c', $lineData['date_end'])));
+                $contract->addLine($line);
+            }
+
+            $dolibarrAccount->setContract($contract);
+        }
+
+        // get bills
+        $billsData = $this->dolibarrService->getBills($dolibarrAccount->getSocId());
+        foreach ($billsData as $billData) {
+            $bill = new DolibarrBill();
+            $bill->setId((int)$billData['id']);
+            $bill->setRef($billData['ref']);
+            $bill->setSocId($dolibarrAccount->getSocId());
+            $bill->setTaxExcludedAmount((float)$billData['total_ht']);
+            $bill->setTaxIncludedAmount((float)$billData['total_ttc']);
+            $bill->setDate(new \DateTime(date('c', $billData['date'])));
+            $bill->setPaid((bool)$billData['paye']);
+            $dolibarrAccount->addBill($bill);
+        }
+
+        return $dolibarrAccount;
+    }
+}

+ 55 - 0
src/DataProvider/Mobyt/MobytUserStatusDataProvider.php

@@ -0,0 +1,55 @@
+<?php
+declare(strict_types=1);
+
+namespace App\DataProvider\Mobyt;
+
+use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
+use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
+use App\ApiResources\Mobyt\MobytUserStatus;
+use App\Repository\Organization\OrganizationRepository;
+use App\Service\Mobyt\MobytService;
+
+/**
+ * Custom provider pour les MobytUserStatus récupérés via l'api Mobyt
+ * @package App\DataProvider\Utils
+ */
+final class MobytUserStatusDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface
+{
+    public function __construct(
+        private MobytService $mobytService,
+        private OrganizationRepository $organizationRepository,
+    ) {}
+
+    public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
+    {
+        return MobytUserStatus::class === $resourceClass;
+    }
+
+    public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): ?MobytUserStatus
+    {
+        $userStatus = new MobytUserStatus();
+        $userStatus->setOrganizationId($id);
+
+        $organization = $this->organizationRepository->find($id);
+        $parameters = $organization->getParameters();
+        $mobytLogin = $parameters->getUsernameSMS();
+        $mobytPassword = $parameters->getPasswordSMS();
+        if (!$mobytLogin) {
+            return $userStatus;
+        }
+
+        $userStatusData = $this->mobytService->getUserStatus($id, $mobytLogin, $mobytPassword);
+        $userStatus->setActive(true);
+        $userStatus->setMoney($userStatusData['money']);
+
+        $topQualitySmsAmount = null;
+        foreach ($userStatusData['sms'] as $_ => $smsTypeData) {
+            // we only retrieve the 'top quality sms', which are identified by the letter L in the mobyt api
+            if ($smsTypeData['type'] === 'L') {
+                $topQualitySmsAmount = $smsTypeData['quantity'];
+            }
+        }
+        $userStatus->setAmount($topQualitySmsAmount);
+        return $userStatus;
+    }
+}

+ 98 - 0
src/Service/Dolibarr/DolibarrService.php

@@ -0,0 +1,98 @@
+<?php
+
+namespace App\Service\Dolibarr;
+
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
+use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
+use Symfony\Contracts\HttpClient\HttpClientInterface;
+
+/**
+ * Service d'appel à l'API dolibarr
+ *
+ * @see https://prod-erp.2iopenservice.com/api/index.php/explorer/
+ */
+class DolibarrService
+{
+    private HttpClientInterface $client;
+
+    function __construct(HttpClientInterface $dolibarr_client)
+    {
+        $this->client = $dolibarr_client;
+    }
+
+    /**
+     * Get a dolibarr society by its opentalent organization id
+     *
+     * @param int $organizationId
+     * @return array
+     */
+    public function getSociety(int $organizationId): array {
+        return $this->request("thirdparties?sqlfilters=" . urlencode("ref_int=" . $organizationId))[0];
+    }
+
+    /**
+     * Get the first active contract for the given dolibarr society
+     *
+     * @param int $socId
+     * @return array|null
+     */
+    public function getActiveContract(int $socId): ?array {
+        try {
+            return $this->request("contracts?limit=1&sqlfilters=statut%3D1&thirdparty_ids%3D" . $socId)[0];
+        } catch (NotFoundHttpException) {
+            return null;
+        }
+    }
+
+    /**
+     * Get a society bills by their society id
+     *
+     * @param int $socId
+     * @return array
+     */
+    public function getBills(int $socId): array {
+        try {
+            return $this->request("invoices?sortfield=datef&sortorder=DESC&limit=5&thirdparty_ids=" . $socId);
+        } catch (NotFoundHttpException) {
+            return [];
+        }
+    }
+
+    /**
+     *
+     * @param $organization
+     * @return array
+     */
+    public function createSociety($organization): array
+    {
+        $body = sprintf(
+            '{"name":"%s","client":"2","code_client":"-1","ref_int":"%s","import_key":"crm"}',
+            $organization->getName(),
+            $organization->getId()
+        );
+
+        return $this->request("api/index.php/thirdparties",'POST', ['body' => $body]);
+    }
+
+    /**
+     * Send an HTTP request to the Dolibarr API,
+     * and return the decoded content of the response's body
+     *
+     * @param string $path
+     * @param string $method
+     * @param array $options
+     * @return array
+     * @throws NotFoundHttpException
+     */
+    private function request(string $path, string $method = 'GET', array $options = []): array
+    {
+        try {
+            $uri = ltrim($path, '/');
+            $response = $this->client->request($method, $uri, $options);
+            return json_decode($response->getContent(), true);
+        } catch (HttpExceptionInterface | TransportExceptionInterface $e) {
+            throw new NotFoundHttpException('data_not_found', $e, 404);
+        }
+    }
+}

+ 67 - 0
src/Service/Mobyt/MobytService.php

@@ -0,0 +1,67 @@
+<?php
+
+namespace App\Service\Mobyt;
+
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
+use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
+use Symfony\Contracts\HttpClient\HttpClientInterface;
+
+/**
+ * Service d'appel à l'API Mobyt
+ */
+class MobytService
+{
+    private HttpClientInterface $client;
+    private string $userId;
+    private string $sessionKey;
+
+    function __construct(
+        HttpClientInterface $mobyt_client
+    )
+    {
+        $this->client = $mobyt_client;
+    }
+
+    private function connect(string $login, string $password){
+        $response = $this->client->request('GET', sprintf('login?username=%s&password=%s', $login, $password));
+        list($this->userId, $this->sessionKey) = explode(';', $response->getContent());
+        return true;
+    }
+
+    /**
+     * Get a dolibarr society by its opentalent organization id
+     *
+     * @param int $organizationId
+     * @return array
+     */
+    public function getUserStatus(int $organizationId, string $login, string $password): array {
+        $this->connect($login, $password);
+        return $this->request(
+            'status?getMoney=true&typeAliases=true',
+            'GET',
+            ['headers' => [ 'user_key' => $this->userId, 'Session_key' => $this->sessionKey ]]
+        );
+    }
+
+    /**
+     * Send an HTTP request to the Dolibarr API,
+     * and return the decoded content of the response's body
+     *
+     * @param string $path
+     * @param string $method
+     * @param array $options
+     * @return array
+     * @throws NotFoundHttpException
+     */
+    private function request(string $path, string $method = 'GET', array $options = []): array
+    {
+        try {
+            $uri = ltrim($path, '/');
+            $response = $this->client->request($method, $uri, $options);
+            return json_decode($response->getContent(), true);
+        } catch (HttpExceptionInterface | TransportExceptionInterface $e) {
+            throw new NotFoundHttpException('data_not_found', $e, 404);
+        }
+    }
+}

+ 2 - 2
src/Service/Utils/GpsCoordinateUtils.php

@@ -24,7 +24,7 @@ class GpsCoordinateUtils
     }
 
     /**
-     * Renvoi un tableau d'adresse existante correspondant à la recherche d'adresse demandée
+     * Renvoie un tableau d'adresse existante correspondant à la recherche d'adresse demandée
      * @param string|null $street
      * @param string|null $cp
      * @param string|null $city
@@ -99,4 +99,4 @@ class GpsCoordinateUtils
         $addressTransformed['cp'] = key_exists('postcode', $address) ? $address['postcode'] : null;
         return $addressTransformed;
     }
-}
+}

+ 15 - 0
symfony.lock

@@ -25,6 +25,9 @@
     "composer/package-versions-deprecated": {
         "version": "1.11.99"
     },
+    "cyclonedx/cyclonedx-php-composer": {
+        "version": "v3.4.1"
+    },
     "doctrine/annotations": {
         "version": "1.0",
         "recipe": {
@@ -179,6 +182,9 @@
             "config/packages/misd_phone_number.yaml"
         ]
     },
+    "package-url/packageurl-php": {
+        "version": "1.0.3"
+    },
     "php": {
         "version": "7.4"
     },
@@ -191,6 +197,9 @@
     "phpdocumentor/type-resolver": {
         "version": "1.4.0"
     },
+    "phplang/scope-exit": {
+        "version": "1.0.0"
+    },
     "psr/cache": {
         "version": "1.0.1"
     },
@@ -209,6 +218,12 @@
     "ruflin/elastica": {
         "version": "3.2"
     },
+    "swaggest/json-diff": {
+        "version": "v3.8.3"
+    },
+    "swaggest/json-schema": {
+        "version": "v0.12.38"
+    },
     "symfony/asset": {
         "version": "v5.1.7"
     },