API improvements (#1826)

This commit is contained in:
Kevin Papst
2020-07-17 20:30:16 +02:00
committed by GitHub
parent 5864c7e127
commit c843dba29a
77 changed files with 1745 additions and 1189 deletions

View File

@@ -12,27 +12,49 @@ namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Table(name="kimai2_activities",
* indexes={
* indexes={
* @ORM\Index(columns={"visible","project_id"}),
* @ORM\Index(columns={"visible","project_id","name"}),
* @ORM\Index(columns={"visible","name"})
* }
* }
* )
* @ORM\Entity(repositoryClass="App\Repository\ActivityRepository")
*
* columns={"visible","name"} => IDX_8811FE1C7AB0E8595E237E06 => activity administration without filter
* columns={"visible","project_id"} => IDX_8811FE1C7AB0E859166D1F9C => activity administration with customer or project filter
* columns={"visible","project_id","name"} => IDX_8811FE1C7AB0E859166D1F9C5E237E06 => activity drop-down for global activities in toolbar or globalsOnly filter in activity administration
* @Serializer\ExclusionPolicy("all")
* @Serializer\VirtualProperty(
* "ProjectName",
* exp="object.getProject() === null ? null : object.getProject().getName()",
* options={
* @Serializer\SerializedName("parentTitle"),
* @Serializer\Type(name="string"),
* @Serializer\Groups({"Activity"})
* }
* )
* @Serializer\VirtualProperty(
* "ProjectAsId",
* exp="object.getProject() === null ? null : object.getProject().getId()",
* options={
* @Serializer\SerializedName("project"),
* @Serializer\Type(name="integer"),
* @Serializer\Groups({"Default"})
* }
* )
*/
class Activity implements EntityWithMetaFields
{
/**
* Internal ID
*
* @var int|null
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
@@ -46,34 +68,82 @@ class Activity implements EntityWithMetaFields
*/
private $project;
/**
* Name of this activity
*
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="name", type="string", length=150, nullable=false)
* @Assert\NotBlank()
* @Assert\Length(min=2, max=150, allowEmptyString=false)
*/
private $name;
/**
* Description of this activity
*
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Activity_Entity"})
*
* @ORM\Column(name="comment", type="text", nullable=true)
*/
private $comment;
/**
* Whether this activity is visible and can be used for timesheets
*
* @var bool
*
* @ORM\Column(name="visible", type="boolean", nullable=false)
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="visible", type="boolean", nullable=false, options={"default": true})
* @Assert\NotNull()
*/
private $visible = true;
// keep the trait include exactly here, for placing the column at the correct position
// keep the traits here, for placing the column at the "correct" position
use ColorTrait;
use BudgetTrait;
/**
* The total monetary budget, will be zero if unconfigured.
*
* @var float
*
* @Serializer\Expose()
* @Serializer\Groups({"Activity_Entity"})
*
* @ORM\Column(name="budget", type="float", nullable=false)
* @Assert\NotNull()
*/
private $budget = 0.00;
/**
* The time budget in seconds, will be be zero if unconfigured.
*
* @var int
*
* @Serializer\Expose()
* @Serializer\Groups({"Activity_Entity"})
*
* @ORM\Column(name="time_budget", type="integer", nullable=false)
* @Assert\NotNull()
*/
private $timeBudget = 0;
/**
* Meta fields
*
* All visible meta (custom) fields registered with this activity
*
* @var ActivityMeta[]|Collection
*
* @Serializer\Expose()
* @Serializer\Groups({"Activity"})
* @Serializer\Type(name="array<App\Entity\ActivityMeta>")
* @Serializer\SerializedName("metaFields")
* @Serializer\Accessor(getter="getVisibleMetaFields")
*
* @ORM\OneToMany(targetEntity="App\Entity\ActivityMeta", mappedBy="activity", cascade={"persist"})
*/
private $meta;
@@ -141,6 +211,30 @@ class Activity implements EntityWithMetaFields
return $this->visible;
}
public function setBudget(float $budget): Activity
{
$this->budget = $budget;
return $this;
}
public function getBudget(): float
{
return $this->budget;
}
public function setTimeBudget(int $seconds): Activity
{
$this->timeBudget = $seconds;
return $this;
}
public function getTimeBudget(): int
{
return $this->timeBudget;
}
/**
* @return Collection|MetaTableTypeInterface[]
*/

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Component\Validator\Constraints as Assert;
/**
@@ -21,6 +22,7 @@ use Symfony\Component\Validator\Constraints as Assert;
* @ORM\UniqueConstraint(columns={"activity_id", "name"})
* }
* )
* @Serializer\ExclusionPolicy("all")
*/
class ActivityMeta implements MetaTableTypeInterface
{

View File

@@ -10,6 +10,7 @@
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
@@ -21,6 +22,8 @@ use Symfony\Component\Validator\Constraints as Assert;
* )
* @ORM\Entity(repositoryClass="App\Repository\ActivityRateRepository")
* @UniqueEntity({"user", "activity"}, ignoreNull=false)
*
* @Serializer\ExclusionPolicy("all")
*/
class ActivityRate implements RateInterface
{
@@ -29,6 +32,8 @@ class ActivityRate implements RateInterface
/**
* @var Activity
*
* @Serializer\Exclude()
*
* @ORM\ManyToOne(targetEntity="App\Entity\Activity")
* @ORM\JoinColumn(onDelete="CASCADE", nullable=false)
* @Assert\NotNull()

View File

@@ -1,69 +0,0 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
trait BudgetTrait
{
/**
* @var float
*
* @ORM\Column(name="budget", type="float", nullable=false)
* @Assert\NotNull()
*/
private $budget = 0.00;
/**
* Time budget in seconds.
*
* @var int
*
* @ORM\Column(name="time_budget", type="integer", nullable=false)
* @Assert\NotNull()
*/
private $timeBudget = 0;
/**
* @param float $budget
* @return self
*/
public function setBudget(?float $budget)
{
$this->budget = $budget;
return $this;
}
/**
* @return float
*/
public function getBudget()
{
return $this->budget;
}
/**
* @param int $seconds
* @return self
*/
public function setTimeBudget(?int $seconds)
{
$this->timeBudget = $seconds;
return $this;
}
public function getTimeBudget(): int
{
return $this->timeBudget;
}
}

View File

@@ -10,13 +10,21 @@
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Component\Validator\Constraints as Assert;
trait ColorTrait
{
/**
* The assigned color in HTML hex format, eg. #dd1d00
*
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="color", type="string", length=7, nullable=true)
* @Assert\Length(min=4, max=7)
*/
private $color = null;

View File

@@ -12,6 +12,8 @@ namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Swagger\Annotations as SWG;
use Symfony\Component\Validator\Constraints as Assert;
/**
@@ -22,14 +24,17 @@ use Symfony\Component\Validator\Constraints as Assert;
* )
* @ORM\Entity(repositoryClass="App\Repository\CustomerRepository")
*
* columns={"visible"} => IDX_5A9760447AB0E859 => used in customer dropdown
* @Serializer\ExclusionPolicy("all")
*/
class Customer implements EntityWithMetaFields
{
public const DEFAULT_CURRENCY = 'EUR';
/**
* @var int
* @var int|null
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
@@ -39,6 +44,9 @@ class Customer implements EntityWithMetaFields
/**
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="name", type="string", length=150, nullable=false)
* @Assert\NotBlank()
* @Assert\Length(min=2, max=150, allowEmptyString=false)
@@ -47,6 +55,9 @@ class Customer implements EntityWithMetaFields
/**
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer_Entity"})
*
* @ORM\Column(name="number", type="string", length=50, nullable=true)
* @Assert\Length(max=50)
*/
@@ -54,12 +65,18 @@ class Customer implements EntityWithMetaFields
/**
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer_Entity"})
*
* @ORM\Column(name="comment", type="text", nullable=true)
*/
private $comment;
/**
* @var bool
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="visible", type="boolean", nullable=false)
* @Assert\NotNull()
*/
@@ -67,6 +84,9 @@ class Customer implements EntityWithMetaFields
/**
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer_Entity"})
*
* @ORM\Column(name="company", type="string", length=255, nullable=true)
* @Assert\Length(max=255)
*/
@@ -74,6 +94,9 @@ class Customer implements EntityWithMetaFields
/**
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer_Entity"})
*
* @ORM\Column(name="vat_id", type="string", length=50, nullable=true)
* @Assert\Length(max=50)
*/
@@ -81,6 +104,9 @@ class Customer implements EntityWithMetaFields
/**
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer_Entity"})
*
* @ORM\Column(name="contact", type="string", length=255, nullable=true)
* @Assert\Length(max=255)
*/
@@ -88,12 +114,18 @@ class Customer implements EntityWithMetaFields
/**
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer_Entity"})
*
* @ORM\Column(name="address", type="text", nullable=true)
*/
private $address;
/**
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer_Entity"})
*
* @ORM\Column(name="country", type="string", length=2, nullable=false)
* @Assert\NotBlank()
* @Assert\Length(max=2)
@@ -102,6 +134,9 @@ class Customer implements EntityWithMetaFields
/**
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer"})
*
* @ORM\Column(name="currency", type="string", length=3, nullable=false)
* @Assert\NotBlank()
* @Assert\Length(max=3)
@@ -110,6 +145,9 @@ class Customer implements EntityWithMetaFields
/**
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer_Entity"})
*
* @ORM\Column(name="phone", type="string", length=255, nullable=true)
* @Assert\Length(max=255)
*/
@@ -117,6 +155,9 @@ class Customer implements EntityWithMetaFields
/**
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer_Entity"})
*
* @ORM\Column(name="fax", type="string", length=255, nullable=true)
* @Assert\Length(max=255)
*/
@@ -124,15 +165,23 @@ class Customer implements EntityWithMetaFields
/**
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer_Entity"})
*
* @ORM\Column(name="mobile", type="string", length=255, nullable=true)
* @Assert\Length(max=255)
*/
private $mobile;
/**
* @var string
* Customers contact email
*
* Limited via RFC to 254 chars
*
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer_Entity"})
*
* @ORM\Column(name="email", type="string", length=255, nullable=true)
* @Assert\Length(max=254)
*/
@@ -140,15 +189,23 @@ class Customer implements EntityWithMetaFields
/**
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer_Entity"})
*
* @ORM\Column(name="homepage", type="string", length=255, nullable=true)
* @Assert\Length(max=255)
*/
private $homepage;
/**
* @var string
* Timezone of begin and end
*
* Length was determined by a MySQL column via "use mysql;describe time_zone_name;"
*
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer_Entity"})
*
* @ORM\Column(name="timezone", type="string", length=64, nullable=false)
* @Assert\NotBlank()
* @Assert\Length(max=64)
@@ -157,17 +214,58 @@ class Customer implements EntityWithMetaFields
// keep the trait include exactly here, for placing the column at the correct position
use ColorTrait;
use BudgetTrait;
/**
* The total monetary budget, will be zero if unconfigured.
*
* @var float
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer_Entity"})
*
* @ORM\Column(name="budget", type="float", nullable=false)
* @Assert\NotNull()
*/
private $budget = 0.00;
/**
* The time budget in seconds, will be be zero if unconfigured.
*
* @var int
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer_Entity"})
*
* @ORM\Column(name="time_budget", type="integer", nullable=false)
* @Assert\NotNull()
*/
private $timeBudget = 0;
/**
* Meta fields
*
* All visible meta (custom) fields registered with this customer
*
* @var CustomerMeta[]|Collection
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer"})
* @Serializer\Type(name="array<App\Entity\CustomerMeta>")
* @Serializer\SerializedName("metaFields")
* @Serializer\Accessor(getter="getVisibleMetaFields")
*
* @ORM\OneToMany(targetEntity="App\Entity\CustomerMeta", mappedBy="customer", cascade={"persist"})
*/
private $meta;
/**
* Teams
*
* If no team is assigned, everyone can access the customer
*
* @var Team[]|ArrayCollection
*
* @Serializer\Expose()
* @Serializer\Groups({"Customer"})
* @SWG\Property(type="array", @SWG\Items(ref="#/definitions/Team"))
*
* @ORM\ManyToMany(targetEntity="Team", cascade={"persist"}, inversedBy="customers")
* @ORM\JoinTable(
* name="kimai2_customers_teams",
@@ -384,6 +482,30 @@ class Customer implements EntityWithMetaFields
return $this->timezone;
}
public function setBudget(float $budget): Customer
{
$this->budget = $budget;
return $this;
}
public function getBudget(): float
{
return $this->budget;
}
public function setTimeBudget(int $seconds): Customer
{
$this->timeBudget = $seconds;
return $this;
}
public function getTimeBudget(): int
{
return $this->timeBudget;
}
/**
* @return Collection|MetaTableTypeInterface[]
*/

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Component\Validator\Constraints as Assert;
/**
@@ -21,6 +22,7 @@ use Symfony\Component\Validator\Constraints as Assert;
* @ORM\UniqueConstraint(columns={"customer_id", "name"})
* }
* )
* @Serializer\ExclusionPolicy("all")
*/
class CustomerMeta implements MetaTableTypeInterface
{

View File

@@ -10,6 +10,7 @@
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
@@ -21,6 +22,8 @@ use Symfony\Component\Validator\Constraints as Assert;
* )
* @ORM\Entity(repositoryClass="App\Repository\CustomerRateRepository")
* @UniqueEntity({"user", "customer"}, ignoreNull=false)
*
* @Serializer\ExclusionPolicy("all")
*/
class CustomerRate implements RateInterface
{
@@ -29,6 +32,8 @@ class CustomerRate implements RateInterface
/**
* @var Customer
*
* @Serializer\Exclude()
*
* @ORM\ManyToOne(targetEntity="App\Entity\Customer")
* @ORM\JoinColumn(onDelete="CASCADE", nullable=false)
* @Assert\NotNull()

View File

@@ -11,6 +11,7 @@ namespace App\Entity;
use App\Form\Type\YesNoType;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Validator\Constraint;
@@ -21,6 +22,8 @@ trait MetaTableTypeTrait
/**
* @var int|null
*
* @Serializer\Exclude()
*
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(name="id", type="integer")
@@ -28,8 +31,13 @@ trait MetaTableTypeTrait
private $id;
/**
* Name of the meta (custom) field
*
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="name", type="string", length=50, nullable=false)
* @Assert\NotNull()
* @Assert\Length(min=2, max=50, allowEmptyString=false)
@@ -37,8 +45,13 @@ trait MetaTableTypeTrait
private $name;
/**
* Value of the meta (custom) field
*
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="value", type="string", length=255, nullable=true)
*/
private $value;

View File

@@ -9,9 +9,12 @@
namespace App\Entity;
use App\Validator\Constraints as Constraints;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Swagger\Annotations as SWG;
use Symfony\Component\Validator\Constraints as Assert;
/**
@@ -22,40 +25,78 @@ use Symfony\Component\Validator\Constraints as Assert;
* }
* )
* @ORM\Entity(repositoryClass="App\Repository\ProjectRepository")
* @App\Validator\Constraints\Project
* @Constraints\Project
*
* columns={"customer_id","visible","name"} => IDX_407F12069395C3F37AB0E8595E237E06 => project administration without filter
* columns={"customer_id","visible","id"} => IDX_407F12069395C3F37AB0E859BF396750 => used in joins between project and customer, eg. dropdowns and activity administration page
* @Serializer\ExclusionPolicy("all")
* @Serializer\VirtualProperty(
* "CustomerName",
* exp="object.getCustomer() === null ? null : object.getCustomer().getName()",
* options={
* @Serializer\SerializedName("parentTitle"),
* @Serializer\Type(name="string"),
* @Serializer\Groups({"Project"})
* }
* )
* @Serializer\VirtualProperty(
* "CustomerAsId",
* exp="object.getCustomer() === null ? null : object.getCustomer().getId()",
* options={
* @Serializer\SerializedName("customer"),
* @Serializer\Type(name="integer"),
* @Serializer\Groups({"Project", "Team", "Not_Expanded"})
* }
* )
*/
class Project implements EntityWithMetaFields
{
/**
* Internal ID
*
* @var int|null
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* Customer for this project
*
* @var Customer
*
* @Serializer\Expose()
* @Serializer\Groups({"Subresource", "Expanded"})
* @SWG\Property(type="array", @SWG\Items(ref="#/definitions/Customer"))
*
* @ORM\ManyToOne(targetEntity="App\Entity\Customer")
* @ORM\JoinColumn(onDelete="CASCADE", nullable=false)
* @Assert\NotNull()
*/
private $customer;
/**
* Project name
*
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="name", type="string", length=150, nullable=false)
* @Assert\NotNull()
* @Assert\Length(min=2, max=150, allowEmptyString=false)
*/
private $name;
/**
* Project order number
*
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Project_Entity"})
*
* @ORM\Column(name="order_number", type="text", length=20, nullable=true)
* @Assert\Length(max=20)
*/
@@ -63,40 +104,60 @@ class Project implements EntityWithMetaFields
/**
* @var \DateTime
*
* @Serializer\Expose()
* @Serializer\Groups({"Project_Entity"})
* @Serializer\Type(name="DateTime")
*
* @ORM\Column(name="order_date", type="datetime", nullable=true)
*/
private $orderDate;
/**
* @var \DateTime
*
* @Serializer\Expose()
* @Serializer\Groups({"Project"})
* @Serializer\Type(name="DateTime")
*
* @ORM\Column(name="start", type="datetime", nullable=true)
*/
private $start;
/**
* @var \DateTime
*
* @Serializer\Expose()
* @Serializer\Groups({"Project"})
* @Serializer\Type(name="DateTime")
*
* @ORM\Column(name="end", type="datetime", nullable=true)
*/
private $end;
/**
* @var string
* @internal used for storing the timezone for "order", "start" and "end" date
*
* @ORM\Column(name="timezone", type="string", length=64, nullable=true)
*/
private $timezone;
/**
* @var bool
* @internal used for having the localization state of the dates (see $timezone)
*/
private $localized = false;
/**
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Project_Entity"})
*
* @ORM\Column(name="comment", type="text", nullable=true)
*/
private $comment;
/**
* @var bool
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="visible", type="boolean", nullable=false)
* @Assert\NotNull()
*/
@@ -104,18 +165,58 @@ class Project implements EntityWithMetaFields
// keep the trait include exactly here, for placing the column at the correct position
use ColorTrait;
use BudgetTrait;
/**
* The total monetary budget, will be zero if unconfigured.
*
* @var float
*
* @Serializer\Expose()
* @Serializer\Groups({"Project_Entity"})
*
* @ORM\Column(name="budget", type="float", nullable=false)
* @Assert\NotNull()
*/
private $budget = 0.00;
/**
* The time budget in seconds, will be be zero if unconfigured.
*
* @var int
*
* @Serializer\Expose()
* @Serializer\Groups({"Project_Entity"})
*
* @ORM\Column(name="time_budget", type="integer", nullable=false)
* @Assert\NotNull()
*/
private $timeBudget = 0;
/**
* Meta fields
*
* All visible meta (custom) fields registered with this project
*
* @var ProjectMeta[]|Collection
*
* @Serializer\Expose()
* @Serializer\Groups({"Project"})
* @Serializer\Type(name="array<App\Entity\ProjectMeta>")
* @Serializer\SerializedName("metaFields")
* @Serializer\Accessor(getter="getVisibleMetaFields")
*
* @ORM\OneToMany(targetEntity="App\Entity\ProjectMeta", mappedBy="project", cascade={"persist"})
*/
private $meta;
/**
* Teams
*
* If no team is assigned, everyone can access the project (also depends on the teams of the customer)
*
* @var Team[]|ArrayCollection
*
* @Serializer\Expose()
* @Serializer\Groups({"Project"})
* @SWG\Property(type="array", @SWG\Items(ref="#/definitions/Team"))
*
* @ORM\ManyToMany(targetEntity="Team", cascade={"persist"}, inversedBy="projects")
* @ORM\JoinTable(
* name="kimai2_projects_teams",
@@ -164,11 +265,7 @@ class Project implements EntityWithMetaFields
return $this->name;
}
/**
* @param string $comment
* @return Project
*/
public function setComment($comment): Project
public function setComment(?string $comment): Project
{
$this->comment = $comment;
@@ -289,6 +386,30 @@ class Project implements EntityWithMetaFields
return $this;
}
public function setBudget(float $budget): Project
{
$this->budget = $budget;
return $this;
}
public function getBudget(): float
{
return $this->budget;
}
public function setTimeBudget(int $seconds): Project
{
$this->timeBudget = $seconds;
return $this;
}
public function getTimeBudget(): int
{
return $this->timeBudget;
}
/**
* @return Collection|MetaTableTypeInterface[]
*/

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Component\Validator\Constraints as Assert;
/**
@@ -21,6 +22,7 @@ use Symfony\Component\Validator\Constraints as Assert;
* @ORM\UniqueConstraint(columns={"project_id", "name"})
* }
* )
* @Serializer\ExclusionPolicy("all")
*/
class ProjectMeta implements MetaTableTypeInterface
{

View File

@@ -10,6 +10,7 @@
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
@@ -21,6 +22,8 @@ use Symfony\Component\Validator\Constraints as Assert;
* )
* @ORM\Entity(repositoryClass="App\Repository\ProjectRateRepository")
* @UniqueEntity({"user", "project"}, ignoreNull=false)
*
* @Serializer\ExclusionPolicy("all")
*/
class ProjectRate implements RateInterface
{
@@ -29,6 +32,8 @@ class ProjectRate implements RateInterface
/**
* @var Project
*
* @Serializer\Exclude()
*
* @ORM\ManyToOne(targetEntity="App\Entity\Project")
* @ORM\JoinColumn(onDelete="CASCADE", nullable=false)
* @Assert\NotNull

View File

@@ -10,6 +10,7 @@
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Swagger\Annotations as SWG;
use Symfony\Component\Validator\Constraints as Assert;
@@ -18,6 +19,9 @@ trait Rate
/**
* @var int|null
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
@@ -26,14 +30,20 @@ trait Rate
/**
* @var User
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
* @SWG\Property(ref="#/definitions/User")
*
* @ORM\ManyToOne(targetEntity="App\Entity\User")
* @ORM\JoinColumn(onDelete="CASCADE", nullable=true)
* @SWG\Property(ref="#/definitions/User")
*/
private $user;
/**
* @var float
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="rate", type="float", nullable=false)
* @Assert\GreaterThanOrEqual(0)
*/
@@ -41,12 +51,18 @@ trait Rate
/**
* @var float|null
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="internal_rate", type="float", nullable=true)
*/
private $internalRate;
/**
* @var bool
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="fixed", type="boolean", nullable=false)
* @Assert\NotNull()
*/

View File

@@ -11,6 +11,7 @@ namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
@@ -22,21 +23,32 @@ use Symfony\Component\Validator\Constraints as Assert;
* )
* @ORM\Entity(repositoryClass="App\Repository\TagRepository")
* @UniqueEntity("name")
*
* @Serializer\ExclusionPolicy("all")
*/
class Tag
{
/**
* The internal ID
*
* @var int
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* The tag name
*
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="name", type="string", length=100, nullable=false)
* @Assert\NotBlank()
* @Assert\Length(min=2, max=100, allowEmptyString=false)
@@ -49,9 +61,11 @@ class Tag
/**
* @var Timesheet[]|ArrayCollection
*
* @Serializer\Exclude()
*
* @ORM\ManyToMany(targetEntity="Timesheet", mappedBy="tags", fetch="EXTRA_LAZY")
*/
protected $timesheets;
private $timesheets;
public function __construct()
{

View File

@@ -12,6 +12,8 @@ namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Swagger\Annotations as SWG;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
@@ -23,48 +25,92 @@ use Symfony\Component\Validator\Constraints as Assert;
* )
* @ORM\Entity(repositoryClass="App\Repository\TeamRepository")
* @UniqueEntity("name")
*
* @Serializer\ExclusionPolicy("all")
*/
class Team
{
/**
* The internal ID
*
* @var int
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* Team name
*
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="name", type="string", length=100, nullable=false)
* @Assert\NotBlank()
* @Assert\Length(min=2, max=100, allowEmptyString=false)
*/
private $name;
/**
* Teamlead
*
* The teamlead for this team
*
* @var User
*
* @Serializer\Expose()
* @Serializer\Groups({"Team_Entity"})
* @SWG\Property(ref="#/definitions/User")
*
* @ORM\ManyToOne(targetEntity="App\Entity\User")
* @ORM\JoinColumn(onDelete="CASCADE", nullable=false)
* @Assert\NotNull()
*/
private $teamlead;
/**
* Team member
*
* All team member, including the teamlead
*
* @var User[]|ArrayCollection
*
* @Serializer\Expose()
* @Serializer\Groups({"Team_Entity"})
* @SWG\Property(type="array", @SWG\Items(ref="#/definitions/User"))
*
* @ORM\ManyToMany(targetEntity="User", mappedBy="teams", fetch="EXTRA_LAZY")
*/
private $users;
/**
* Customers
*
* All customers assigned to the team
*
* @var Customer[]|ArrayCollection
*
* @Serializer\Expose()
* @Serializer\Groups({"Team_Entity"})
* @SWG\Property(type="array", @SWG\Items(ref="#/definitions/Customer"))
*
* @ORM\ManyToMany(targetEntity="Customer", mappedBy="teams", fetch="EXTRA_LAZY")
*/
private $customers;
/**
* Projects
*
* All projects assigned to the team
*
* @var Project[]|ArrayCollection
*
* @Serializer\Expose()
* @Serializer\Groups({"Team_Entity", "Expanded"})
* @SWG\Property(type="array", @SWG\Items(ref="#/definitions/Project"))
*
* @ORM\ManyToMany(targetEntity="Project", mappedBy="teams", fetch="EXTRA_LAZY")
*/
private $projects;

View File

@@ -10,12 +10,15 @@
namespace App\Entity;
use App\Export\ExportItemInterface;
use App\Validator\Constraints as Constraints;
use DateTime;
use DateTimeZone;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use JMS\Serializer\Annotation as Serializer;
use Swagger\Annotations as SWG;
use Symfony\Component\Validator\Constraints as Assert;
/**
@@ -31,7 +34,45 @@ use Symfony\Component\Validator\Constraints as Assert;
* )
* @ORM\Entity(repositoryClass="App\Repository\TimesheetRepository")
* @ORM\HasLifecycleCallbacks()
* @App\Validator\Constraints\Timesheet
* @Constraints\Timesheet
*
* @Serializer\ExclusionPolicy("all")
* @Serializer\VirtualProperty(
* "ActivityAsId",
* exp="object.getActivity() === null ? null : object.getActivity().getId()",
* options={
* @Serializer\SerializedName("activity"),
* @Serializer\Type(name="integer"),
* @Serializer\Groups({"Not_Expanded"})
* }
* )
* @Serializer\VirtualProperty(
* "ProjectAsId",
* exp="object.getProject() === null ? null : object.getProject().getId()",
* options={
* @Serializer\SerializedName("project"),
* @Serializer\Type(name="integer"),
* @Serializer\Groups({"Not_Expanded"})
* }
* )
* @Serializer\VirtualProperty(
* "UserAsId",
* exp="object.getUser().getId()",
* options={
* @Serializer\SerializedName("user"),
* @Serializer\Type(name="integer"),
* @Serializer\Groups({"Default"})
* }
* )
* @Serializer\VirtualProperty(
* "TagsAsArray",
* exp="object.getTagsAsArray()",
* options={
* @Serializer\SerializedName("tags"),
* @Serializer\Type(name="array<string>"),
* @Serializer\Groups({"Default"})
* }
* )
*/
class Timesheet implements EntityWithMetaFields, ExportItemInterface
{
@@ -59,47 +100,57 @@ class Timesheet implements EntityWithMetaFields, ExportItemInterface
/**
* @var int|null
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var DateTime
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
* @Serializer\Type(name="DateTime")
*
* @ORM\Column(name="start_time", type="datetime", nullable=false)
* @Assert\NotNull()
*/
private $begin;
/**
* @var DateTime
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
* @Serializer\Type(name="DateTime")
*
* @ORM\Column(name="end_time", type="datetime", nullable=true)
*/
private $end;
/**
* @var string
* @internal for storing the timezone of "begin" and "end" date
*
* @ORM\Column(name="timezone", type="string", length=64, nullable=false)
*/
private $timezone;
/**
* @var bool
* @internal for storing the localized state of dates (see $timezone)
*/
private $localized = false;
/**
* @var int
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="duration", type="integer", nullable=true)
* @Assert\GreaterThanOrEqual(0)
*/
private $duration = 0;
/**
* @var User
*
@@ -108,71 +159,92 @@ class Timesheet implements EntityWithMetaFields, ExportItemInterface
* @Assert\NotNull()
*/
private $user;
/**
* Activity
*
* @var Activity
*
* @Serializer\Expose()
* @Serializer\Groups({"Expanded"})
* @SWG\Property(type="array", @SWG\Items(ref="#/definitions/ActivityExpanded"))
*
* @ORM\ManyToOne(targetEntity="App\Entity\Activity")
* @ORM\JoinColumn(onDelete="CASCADE", nullable=false)
* @Assert\NotNull()
*/
private $activity;
/**
* Project
*
* @var Project
*
* @Serializer\Expose()
* @Serializer\Groups({"Subresource", "Expanded"})
* @SWG\Property(type="array", @SWG\Items(ref="#/definitions/ProjectExpanded"))
*
* @ORM\ManyToOne(targetEntity="App\Entity\Project")
* @ORM\JoinColumn(onDelete="CASCADE", nullable=false)
* @Assert\NotNull()
*/
private $project;
/**
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="description", type="text", nullable=true)
*/
private $description;
/**
* @var float
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="rate", type="float", nullable=false)
* @Assert\GreaterThanOrEqual(0)
*/
private $rate = 0.00;
/**
* @var float|null
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="internal_rate", type="float", nullable=true)
*/
private $internalRate;
/**
* @var float|null
*
* @Serializer\Expose()
* @Serializer\Groups({"Entity"})
*
* @ORM\Column(name="fixed_rate", type="float", nullable=true)
* @Assert\GreaterThanOrEqual(0)
*/
private $fixedRate = null;
/**
* @var float
*
* @Serializer\Expose()
* @Serializer\Groups({"Entity"})
*
* @ORM\Column(name="hourly_rate", type="float", nullable=true)
* @Assert\GreaterThanOrEqual(0)
*/
private $hourlyRate = null;
/**
* @var bool
*
* @Serializer\Expose()
* @Serializer\Groups({"Entity"})
*
* @ORM\Column(name="exported", type="boolean", nullable=false)
* @Assert\NotNull()
*/
private $exported = false;
/**
* @var bool
*
@@ -180,7 +252,6 @@ class Timesheet implements EntityWithMetaFields, ExportItemInterface
* @Assert\NotNull()
*/
private $billable = true;
/**
* @var string
*
@@ -188,16 +259,17 @@ class Timesheet implements EntityWithMetaFields, ExportItemInterface
* @Assert\NotNull()
*/
private $category = self::WORK;
/**
* @var DateTime|null
* @internal used for limiting queries, eg. via API sync
*
* @Gedmo\Timestampable
* @ORM\Column(name="modified_at", type="datetime", nullable=true)
*/
private $modifiedAt;
/**
* Tags
*
* @var Tag[]|ArrayCollection
*
* @ORM\ManyToMany(targetEntity="App\Entity\Tag", inversedBy="timesheets", cascade={"persist"})
@@ -213,10 +285,19 @@ class Timesheet implements EntityWithMetaFields, ExportItemInterface
* @Assert\Valid()
*/
private $tags;
/**
* Meta fields
*
* All visible meta (custom) fields registered with this timesheet
*
* @var TimesheetMeta[]|Collection
*
* @Serializer\Expose()
* @Serializer\Groups({"Timesheet"})
* @Serializer\Type(name="array<App\Entity\TimesheetMeta>")
* @Serializer\SerializedName("metaFields")
* @Serializer\Accessor(getter="getVisibleMetaFields")
*
* @ORM\OneToMany(targetEntity="App\Entity\TimesheetMeta", mappedBy="timesheet", cascade={"persist"})
*/
private $meta;

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Component\Validator\Constraints as Assert;
/**
@@ -21,6 +22,7 @@ use Symfony\Component\Validator\Constraints as Assert;
* @ORM\UniqueConstraint(columns={"timesheet_id", "name"})
* }
* )
* @Serializer\ExclusionPolicy("all")
*/
class TimesheetMeta implements MetaTableTypeInterface
{

View File

@@ -14,6 +14,7 @@ use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
@@ -28,6 +29,26 @@ use Symfony\Component\Validator\Constraints as Assert;
* )
* @UniqueEntity("username")
* @UniqueEntity("email")
*
* @Serializer\ExclusionPolicy("all")
* @Serializer\VirtualProperty(
* "LanguageAsString",
* exp="object.getLocale()",
* options={
* @Serializer\SerializedName("language"),
* @Serializer\Type(name="string"),
* @Serializer\Groups({"User_Entity"})
* }
* )
* @Serializer\VirtualProperty(
* "TimezoneAsString",
* exp="object.getTimezone()",
* options={
* @Serializer\SerializedName("timezone"),
* @Serializer\Type(name="string"),
* @Serializer\Groups({"User_Entity"})
* }
* )
*/
class User extends BaseUser implements UserInterface
{
@@ -44,67 +65,94 @@ class User extends BaseUser implements UserInterface
public const AUTH_SAML = 'saml';
/**
* Internal ID
*
* @var int
* @internal must be protected because of parent class
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(name="id", type="integer")
*/
protected $id;
/**
* The user alias will be displayed in the frontend instead of the username
*
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"Default"})
*
* @ORM\Column(name="alias", type="string", length=60, nullable=true)
* @Assert\Length(max=60)
*/
private $alias;
/**
* Registration date for the user
*
* @var \DateTime
*
* @ORM\Column(name="registration_date", type="datetime", nullable=true)
*/
private $registeredAt;
/**
* An additional title for the user, like the Job position or Department
*
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"User_Entity"})
*
* @ORM\Column(name="title", type="string", length=50, nullable=true)
* @Assert\Length(max=50)
*/
private $title;
/**
* URL to the users avatar, will be auto-generated if empty
*
* @var string
*
* @Serializer\Expose()
* @Serializer\Groups({"User_Entity"})
*
* @ORM\Column(name="avatar", type="string", length=255, nullable=true)
* @Assert\Length(max=255)
*/
private $avatar;
/**
* API token (password) for this user
*
* @var string
*
* @ORM\Column(name="api_token", type="string", length=255, nullable=true)
*/
protected $apiToken;
private $apiToken;
/**
* @var string
* @internal to be set via form, must not be persisted
*/
protected $plainApiToken;
private $plainApiToken;
/**
* User preferences
*
* List of preferences for this user, required ones have dedicated fields/methods
*
* @var UserPreference[]|Collection
*
* @ORM\OneToMany(targetEntity="App\Entity\UserPreference", mappedBy="user", cascade={"persist"})
*/
private $preferences;
/**
* All teams of the user
*
* @var Team[]|ArrayCollection
*
* @Serializer\Expose()
* @Serializer\Groups({"User_Entity"})
*
* @ORM\ManyToMany(targetEntity="Team", inversedBy="users", cascade={"persist"})
* @ORM\JoinTable(
* name="kimai2_users_teams",
@@ -117,25 +165,24 @@ class User extends BaseUser implements UserInterface
* )
*/
private $teams;
/**
* The type of authentication used by the user (eg. "kimai", "ldap", "saml")
*
* @var string
* @internal for internal usage only
*
* @ORM\Column(name="auth", type="string", length=20, nullable=true)
* @Assert\Length(max=20)
*/
private $auth = self::AUTH_INTERNAL;
/**
* This flag will be initialized in UserEnvironmentSubscriber.
*
* @var bool|null
* @internal has no database mapping. as the value is calculated from a permission
*/
private $isAllowedToSeeAllData = null;
/**
* User constructor.
*/
public function __construct()
{
parent::__construct();