Comptabilite (Super Admin) : - ComptabiliteController avec 7 exports CSV/JSON compatibles SAGE (journal ventes, grand livre, FEC, balance agee, reglements, commissions Stripe 1.5%+0.25E, couts services) - Export PDF via ComptaPdf (FPDF) avec bloc legal pre-rempli, tableau pagine, champ signature DocuSeal - Signature electronique DocuSeal + callback + envoi email signe avec template dedie (compta_export_signed.html.twig) - Rapport financier public (RapportFinancierPdf) : recettes par service, depenses (Stripe, infra, prestataires), bilan excedent/deficit - Codes comptables clients EC-XXXX (plus de 411xxx) Prestataires (Super Admin) : - Entite Prestataire (raisonSociale, siret, email, phone, adresse) - Entite FacturePrestataire (numFacture, montantHt, montantTtc, year, month, isPaid, PDF via Vich) - CRUD complet avec recherche SIRET via proxy API data.gouv.fr - Commande cron app:reminder:factures-prestataire (5 du mois) - Factures prestataires integrees dans export couts services - Sidebar Super Admin : entree Prestataires + Comptabilite Stats (/admin/stats) : - Cout prestataire dynamique depuis FacturePrestataire - Fusion Infra + Prestataire en "Cout de fonctionnement" - Commission Stripe corrigee (1.5% + 0.25E par transaction) Divers : - DocuSealService::sendComptaForSignature() + getApi() - Customer::generateCodeComptable() format EC-XXXX-XXXXX - Protection double prefixe EC- a la creation client - Bouton regenerer PDF cache quand advert state=accepted - Modals sans script inline (data-modal-open/close dans app.js) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
444 lines
9.5 KiB
PHP
444 lines
9.5 KiB
PHP
<?php
|
|
|
|
namespace App\Entity;
|
|
|
|
use App\Repository\CustomerRepository;
|
|
use Doctrine\ORM\Mapping as ORM;
|
|
|
|
#[ORM\Entity(repositoryClass: CustomerRepository::class)]
|
|
class Customer
|
|
{
|
|
public const TYPE_ASSO = 'association';
|
|
public const TYPE_IE = 'auto-entrepreneur';
|
|
public const TYPE_SAS = 'sas';
|
|
public const TYPE_SARL = 'sarl';
|
|
public const TYPE_EURL = 'eurl';
|
|
public const TYPE_SA = 'sa';
|
|
public const TYPE_SCI = 'sci';
|
|
public const TYPE_PARTICULIER = 'particulier';
|
|
|
|
public const TYPES = [
|
|
self::TYPE_ASSO,
|
|
self::TYPE_IE,
|
|
self::TYPE_SAS,
|
|
self::TYPE_SARL,
|
|
self::TYPE_EURL,
|
|
self::TYPE_SA,
|
|
self::TYPE_SCI,
|
|
self::TYPE_PARTICULIER,
|
|
];
|
|
|
|
public const STATE_ACTIVE = 'active';
|
|
public const STATE_SUSPENDED = 'suspended';
|
|
public const STATE_DISABLED = 'disabled';
|
|
public const STATE_PENDING_DELETE = 'pending_delete';
|
|
|
|
public const STATES = [
|
|
self::STATE_ACTIVE,
|
|
self::STATE_SUSPENDED,
|
|
self::STATE_DISABLED,
|
|
self::STATE_PENDING_DELETE,
|
|
];
|
|
|
|
#[ORM\Id]
|
|
#[ORM\GeneratedValue]
|
|
#[ORM\Column]
|
|
private ?int $id = null;
|
|
|
|
#[ORM\OneToOne(targetEntity: User::class)]
|
|
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
|
private User $user;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $raisonSociale = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $firstName = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $lastName = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $email = null;
|
|
|
|
#[ORM\Column(length: 20, nullable: true)]
|
|
private ?string $phone = null;
|
|
|
|
#[ORM\Column(length: 500, nullable: true)]
|
|
private ?string $address = null;
|
|
|
|
#[ORM\Column(length: 500, nullable: true)]
|
|
private ?string $address2 = null;
|
|
|
|
#[ORM\Column(length: 10, nullable: true)]
|
|
private ?string $zipCode = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $city = null;
|
|
|
|
#[ORM\Column(type: 'decimal', precision: 10, scale: 7, nullable: true)]
|
|
private ?string $geoLat = null;
|
|
|
|
#[ORM\Column(type: 'decimal', precision: 10, scale: 7, nullable: true)]
|
|
private ?string $geoLong = null;
|
|
|
|
#[ORM\Column(length: 14, nullable: true)]
|
|
private ?string $siret = null;
|
|
|
|
#[ORM\Column(length: 50, nullable: true)]
|
|
private ?string $rcs = null;
|
|
|
|
#[ORM\Column(length: 20, nullable: true)]
|
|
private ?string $numTva = null;
|
|
|
|
#[ORM\Column(length: 10, nullable: true)]
|
|
private ?string $ape = null;
|
|
|
|
#[ORM\Column(length: 20, nullable: true)]
|
|
private ?string $rna = null;
|
|
|
|
#[ORM\Column(length: 255, nullable: true)]
|
|
private ?string $stripeCustomerId = null;
|
|
|
|
#[ORM\Column(length: 30, nullable: true)]
|
|
private ?string $typeCompany = null;
|
|
|
|
#[ORM\Column(length: 20)]
|
|
private string $state = self::STATE_ACTIVE;
|
|
|
|
#[ORM\Column]
|
|
private \DateTimeImmutable $createdAt;
|
|
|
|
#[ORM\Column(length: 50, unique: true, nullable: true)]
|
|
private ?string $codeComptable = null;
|
|
|
|
#[ORM\Column(length: 10, nullable: true)]
|
|
private ?string $revendeurCode = null;
|
|
|
|
#[ORM\Column(nullable: true)]
|
|
private ?\DateTimeImmutable $updatedAt = null;
|
|
|
|
public function __construct(User $user)
|
|
{
|
|
$this->user = $user;
|
|
$this->createdAt = new \DateTimeImmutable();
|
|
}
|
|
|
|
public function getCodeComptable(): ?string
|
|
{
|
|
return $this->codeComptable;
|
|
}
|
|
|
|
public function setCodeComptable(?string $codeComptable): static
|
|
{
|
|
$this->codeComptable = $codeComptable;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getRevendeurCode(): ?string
|
|
{
|
|
return $this->revendeurCode;
|
|
}
|
|
|
|
public function setRevendeurCode(?string $revendeurCode): static
|
|
{
|
|
$this->revendeurCode = $revendeurCode;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function generateCodeComptable(): string
|
|
{
|
|
$idPart = str_pad((string) ($this->id ?? 0), 4, '0', STR_PAD_LEFT);
|
|
$namePart = '';
|
|
|
|
if (null !== $this->raisonSociale && '' !== $this->raisonSociale) {
|
|
$clean = preg_replace('/[^A-Za-z]/', '', $this->raisonSociale);
|
|
$namePart = strtoupper(substr($clean, 0, 5));
|
|
} elseif (null !== $this->lastName && '' !== $this->lastName) {
|
|
$clean = preg_replace('/[^A-Za-z]/', '', $this->lastName);
|
|
$namePart = strtoupper(substr($clean, 0, 5));
|
|
}
|
|
|
|
$namePart = str_pad($namePart, 5, 'X');
|
|
|
|
return 'EC-'.$idPart.'-'.$namePart;
|
|
}
|
|
|
|
public function getId(): ?int
|
|
{
|
|
return $this->id;
|
|
}
|
|
|
|
public function getUser(): User
|
|
{
|
|
return $this->user;
|
|
}
|
|
|
|
public function getRaisonSociale(): ?string
|
|
{
|
|
return $this->raisonSociale;
|
|
}
|
|
|
|
public function setRaisonSociale(?string $raisonSociale): static
|
|
{
|
|
$this->raisonSociale = $raisonSociale;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getFirstName(): ?string
|
|
{
|
|
return $this->firstName;
|
|
}
|
|
|
|
public function setFirstName(?string $firstName): static
|
|
{
|
|
$this->firstName = $firstName;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getLastName(): ?string
|
|
{
|
|
return $this->lastName;
|
|
}
|
|
|
|
public function setLastName(?string $lastName): static
|
|
{
|
|
$this->lastName = $lastName;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getFullName(): string
|
|
{
|
|
if (null !== $this->raisonSociale && '' !== $this->raisonSociale) {
|
|
return $this->raisonSociale;
|
|
}
|
|
|
|
return trim(($this->firstName ?? '').' '.($this->lastName ?? ''));
|
|
}
|
|
|
|
public function getEmail(): ?string
|
|
{
|
|
return $this->email;
|
|
}
|
|
|
|
public function setEmail(?string $email): static
|
|
{
|
|
$this->email = $email;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getPhone(): ?string
|
|
{
|
|
return $this->phone;
|
|
}
|
|
|
|
public function setPhone(?string $phone): static
|
|
{
|
|
$this->phone = $phone;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getAddress(): ?string
|
|
{
|
|
return $this->address;
|
|
}
|
|
|
|
public function setAddress(?string $address): static
|
|
{
|
|
$this->address = $address;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getAddress2(): ?string
|
|
{
|
|
return $this->address2;
|
|
}
|
|
|
|
public function setAddress2(?string $address2): static
|
|
{
|
|
$this->address2 = $address2;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getZipCode(): ?string
|
|
{
|
|
return $this->zipCode;
|
|
}
|
|
|
|
public function setZipCode(?string $zipCode): static
|
|
{
|
|
$this->zipCode = $zipCode;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getCity(): ?string
|
|
{
|
|
return $this->city;
|
|
}
|
|
|
|
public function setCity(?string $city): static
|
|
{
|
|
$this->city = $city;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getGeoLat(): ?string
|
|
{
|
|
return $this->geoLat;
|
|
}
|
|
|
|
public function setGeoLat(?string $geoLat): static
|
|
{
|
|
$this->geoLat = $geoLat;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getGeoLong(): ?string
|
|
{
|
|
return $this->geoLong;
|
|
}
|
|
|
|
public function setGeoLong(?string $geoLong): static
|
|
{
|
|
$this->geoLong = $geoLong;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getSiret(): ?string
|
|
{
|
|
return $this->siret;
|
|
}
|
|
|
|
public function setSiret(?string $siret): static
|
|
{
|
|
$this->siret = $siret;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getRcs(): ?string
|
|
{
|
|
return $this->rcs;
|
|
}
|
|
|
|
public function setRcs(?string $rcs): static
|
|
{
|
|
$this->rcs = $rcs;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getNumTva(): ?string
|
|
{
|
|
return $this->numTva;
|
|
}
|
|
|
|
public function setNumTva(?string $numTva): static
|
|
{
|
|
$this->numTva = $numTva;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getApe(): ?string
|
|
{
|
|
return $this->ape;
|
|
}
|
|
|
|
public function setApe(?string $ape): static
|
|
{
|
|
$this->ape = $ape;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getRna(): ?string
|
|
{
|
|
return $this->rna;
|
|
}
|
|
|
|
public function setRna(?string $rna): static
|
|
{
|
|
$this->rna = $rna;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getStripeCustomerId(): ?string
|
|
{
|
|
return $this->stripeCustomerId;
|
|
}
|
|
|
|
public function setStripeCustomerId(?string $stripeCustomerId): static
|
|
{
|
|
$this->stripeCustomerId = $stripeCustomerId;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getTypeCompany(): ?string
|
|
{
|
|
return $this->typeCompany;
|
|
}
|
|
|
|
public function setTypeCompany(?string $typeCompany): static
|
|
{
|
|
$this->typeCompany = $typeCompany;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getState(): string
|
|
{
|
|
return $this->state;
|
|
}
|
|
|
|
public function setState(string $state): static
|
|
{
|
|
$this->state = $state;
|
|
$this->updatedAt = new \DateTimeImmutable();
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function isActive(): bool
|
|
{
|
|
return self::STATE_ACTIVE === $this->state;
|
|
}
|
|
|
|
public function isPendingDelete(): bool
|
|
{
|
|
return self::STATE_PENDING_DELETE === $this->state;
|
|
}
|
|
|
|
public function getCreatedAt(): \DateTimeImmutable
|
|
{
|
|
return $this->createdAt;
|
|
}
|
|
|
|
public function getUpdatedAt(): ?\DateTimeImmutable
|
|
{
|
|
return $this->updatedAt;
|
|
}
|
|
|
|
public function setUpdatedAt(?\DateTimeImmutable $updatedAt): static
|
|
{
|
|
$this->updatedAt = $updatedAt;
|
|
|
|
return $this;
|
|
}
|
|
}
|