Ensure tax codes that are sent are correct

This commit is contained in:
David Bomba 2025-08-14 14:41:21 +10:00
parent 084f0fea25
commit 70dea557f5
9 changed files with 385 additions and 57 deletions

View File

@ -158,12 +158,16 @@ class EntityLevel implements EntityLevelInterface
continue; continue;
} }
if($field == 'vat_number' && $client->classification == 'individual') {
continue;
}
$errors[] = ['field' => $field, 'label' => ctrans("texts.{$field}")]; $errors[] = ['field' => $field, 'label' => ctrans("texts.{$field}")];
} }
//If not an individual, you MUST have a VAT number if you are in the EU //If not an individual, you MUST have a VAT number if you are in the EU
if (!$this->validString($client->vat_number)) { if ($client->classification != 'individual' && !$this->validString($client->vat_number)) {
$errors[] = ['field' => 'vat_number', 'label' => ctrans("texts.vat_number")]; $errors[] = ['field' => 'vat_number', 'label' => ctrans("texts.vat_number")];
} }

View File

@ -16,7 +16,17 @@ class Desglose extends BaseXmlModel
{ {
$root = $this->createElement($doc, 'Desglose'); $root = $this->createElement($doc, 'Desglose');
// If we have a DetalleDesglose object, use it // If we have DetalleDesglose objects in the desgloseIVA array, use them
if ($this->desgloseIVA !== null && is_array($this->desgloseIVA) && count($this->desgloseIVA) > 0) {
foreach ($this->desgloseIVA as $detalleDesglose) {
if ($detalleDesglose instanceof DetalleDesglose) {
$root->appendChild($detalleDesglose->toXml($doc));
}
}
return $root;
}
// If we have a single DetalleDesglose object, use it
if ($this->detalleDesglose !== null) { if ($this->detalleDesglose !== null) {
$root->appendChild($this->detalleDesglose->toXml($doc)); $root->appendChild($this->detalleDesglose->toXml($doc));
return $root; return $root;
@ -36,7 +46,7 @@ class Desglose extends BaseXmlModel
} }
// Add ClaveRegimen if present // Add ClaveRegimen if present
if (isset($this->desgloseFactura['ClaveRegimen'])) { if (isset($this->desgloseFactura['ClaveRegimen']) ) {
$detalleDesglose->appendChild($this->createElement($doc, 'ClaveRegimen', $this->desgloseFactura['ClaveRegimen'])); $detalleDesglose->appendChild($this->createElement($doc, 'ClaveRegimen', $this->desgloseFactura['ClaveRegimen']));
} else { } else {
// Default ClaveRegimen // Default ClaveRegimen
@ -48,29 +58,28 @@ class Desglose extends BaseXmlModel
$this->desgloseFactura['CalificacionOperacion'] ?? 'S1')); $this->desgloseFactura['CalificacionOperacion'] ?? 'S1'));
// Add TipoImpositivo if present // Add TipoImpositivo if present
if (isset($this->desgloseFactura['TipoImpositivo'])) { if (isset($this->desgloseFactura['TipoImpositivo']) && $this->desgloseFactura['CalificacionOperacion'] == 'S1') {
$detalleDesglose->appendChild($this->createElement($doc, 'TipoImpositivo', $detalleDesglose->appendChild($this->createElement($doc, 'TipoImpositivo',
number_format((float)$this->desgloseFactura['TipoImpositivo'], 2, '.', ''))); number_format((float)$this->desgloseFactura['TipoImpositivo'], 2, '.', '')));
} else {
// Default TipoImpositivo
$detalleDesglose->appendChild($this->createElement($doc, 'TipoImpositivo', '21.00'));
} }
// else {
// // Default TipoImpositivo
// $detalleDesglose->appendChild($this->createElement($doc, 'TipoImpositivo', '0'));
// }
// Convert BaseImponible to BaseImponibleOimporteNoSujeto if needed // Convert BaseImponible to BaseImponibleOimporteNoSujeto if needed
$baseImponible = isset($this->desgloseFactura['BaseImponible']) $baseImponible = isset($this->desgloseFactura['BaseImponible'])
? $this->desgloseFactura['BaseImponible'] ? $this->desgloseFactura['BaseImponible']
: ($this->desgloseFactura['BaseImponibleOimporteNoSujeto'] ?? '100.00'); : ($this->desgloseFactura['BaseImponibleOimporteNoSujeto'] ?? '0');
$detalleDesglose->appendChild($this->createElement($doc, 'BaseImponibleOimporteNoSujeto', $detalleDesglose->appendChild($this->createElement($doc, 'BaseImponibleOimporteNoSujeto',
number_format((float)$baseImponible, 2, '.', ''))); number_format((float)$baseImponible, 2, '.', '')));
// Convert Cuota to CuotaRepercutida if needed
$cuota = isset($this->desgloseFactura['Cuota'])
? $this->desgloseFactura['Cuota']
: ($this->desgloseFactura['CuotaRepercutida'] ?? '21.00');
$detalleDesglose->appendChild($this->createElement($doc, 'CuotaRepercutida', if(isset($this->desgloseFactura['Cuota']) && $this->desgloseFactura['CalificacionOperacion'] == 'S1'){
number_format((float)$cuota, 2, '.', ''))); $detalleDesglose->appendChild($this->createElement($doc, 'CuotaRepercutida',
number_format((float)$this->desgloseFactura['Cuota'], 2, '.', '')));
}
// Add TipoRecargoEquivalencia if present // Add TipoRecargoEquivalencia if present
if (isset($this->desgloseFactura['TipoRecargoEquivalencia'])) { if (isset($this->desgloseFactura['TipoRecargoEquivalencia'])) {
@ -110,7 +119,7 @@ class Desglose extends BaseXmlModel
// Convert BaseImponible to BaseImponibleOimporteNoSujeto if needed // Convert BaseImponible to BaseImponibleOimporteNoSujeto if needed
$baseImponible = isset($desglose['BaseImponible']) $baseImponible = isset($desglose['BaseImponible'])
? $desglose['BaseImponible'] ? $desglose['BaseImponible']
: ($desglose['BaseImponibleOimporteNoSujeto'] ?? '100.00'); : ($desglose['BaseImponibleOimporteNoSujeto'] ?? '0');
$detalleDesglose->appendChild($this->createElement($doc, 'BaseImponibleOimporteNoSujeto', $detalleDesglose->appendChild($this->createElement($doc, 'BaseImponibleOimporteNoSujeto',
number_format((float)$baseImponible, 2, '.', ''))); number_format((float)$baseImponible, 2, '.', '')));
@ -118,7 +127,7 @@ class Desglose extends BaseXmlModel
// Convert Cuota to CuotaRepercutida if needed // Convert Cuota to CuotaRepercutida if needed
$cuota = isset($desglose['Cuota']) $cuota = isset($desglose['Cuota'])
? $desglose['Cuota'] ? $desglose['Cuota']
: ($desglose['CuotaRepercutida'] ?? '21.00'); : ($desglose['CuotaRepercutida'] ?? '0');
$detalleDesglose->appendChild($this->createElement($doc, 'CuotaRepercutida', $detalleDesglose->appendChild($this->createElement($doc, 'CuotaRepercutida',
number_format((float)$cuota, 2, '.', ''))); number_format((float)$cuota, 2, '.', '')));
@ -147,7 +156,7 @@ class Desglose extends BaseXmlModel
// Convert BaseImponible to BaseImponibleOimporteNoSujeto if needed // Convert BaseImponible to BaseImponibleOimporteNoSujeto if needed
$baseImponible = isset($this->desgloseIVA['BaseImponible']) $baseImponible = isset($this->desgloseIVA['BaseImponible'])
? $this->desgloseIVA['BaseImponible'] ? $this->desgloseIVA['BaseImponible']
: ($this->desgloseIVA['BaseImponibleOimporteNoSujeto'] ?? '100.00'); : ($this->desgloseIVA['BaseImponibleOimporteNoSujeto'] ?? '');
$detalleDesglose->appendChild($this->createElement($doc, 'BaseImponibleOimporteNoSujeto', $detalleDesglose->appendChild($this->createElement($doc, 'BaseImponibleOimporteNoSujeto',
number_format((float)$baseImponible, 2, '.', ''))); number_format((float)$baseImponible, 2, '.', '')));
@ -155,7 +164,7 @@ class Desglose extends BaseXmlModel
// Convert Cuota to CuotaRepercutida if needed // Convert Cuota to CuotaRepercutida if needed
$cuota = isset($this->desgloseIVA['Cuota']) $cuota = isset($this->desgloseIVA['Cuota'])
? $this->desgloseIVA['Cuota'] ? $this->desgloseIVA['Cuota']
: ($this->desgloseIVA['CuotaRepercutida'] ?? '21.00'); : ($this->desgloseIVA['CuotaRepercutida'] ?? '0');
$detalleDesglose->appendChild($this->createElement($doc, 'CuotaRepercutida', $detalleDesglose->appendChild($this->createElement($doc, 'CuotaRepercutida',
number_format((float)$cuota, 2, '.', ''))); number_format((float)$cuota, 2, '.', '')));
@ -164,16 +173,16 @@ class Desglose extends BaseXmlModel
} }
} }
// If we still don't have any data, create a default DetalleDesglose // // If we still don't have any data, create a default DetalleDesglose
if (!$detalleDesglose->hasChildNodes()) { // if (!$detalleDesglose->hasChildNodes()) {
// Create a default DetalleDesglose with basic IVA information // // Create a default DetalleDesglose with basic IVA information
$detalleDesglose->appendChild($this->createElement($doc, 'Impuesto', '01')); // $detalleDesglose->appendChild($this->createElement($doc, 'Impuesto', '01'));
$detalleDesglose->appendChild($this->createElement($doc, 'ClaveRegimen', '01')); // $detalleDesglose->appendChild($this->createElement($doc, 'ClaveRegimen', '01'));
$detalleDesglose->appendChild($this->createElement($doc, 'CalificacionOperacion', 'S1')); // $detalleDesglose->appendChild($this->createElement($doc, 'CalificacionOperacion', 'S1'));
$detalleDesglose->appendChild($this->createElement($doc, 'TipoImpositivo', '21.00')); // $detalleDesglose->appendChild($this->createElement($doc, 'TipoImpositivo', '0'));
$detalleDesglose->appendChild($this->createElement($doc, 'BaseImponibleOimporteNoSujeto', '100.00')); // $detalleDesglose->appendChild($this->createElement($doc, 'BaseImponibleOimporteNoSujeto', '0'));
$detalleDesglose->appendChild($this->createElement($doc, 'CuotaRepercutida', '21.00')); // $detalleDesglose->appendChild($this->createElement($doc, 'CuotaRepercutida', '0'));
} // }
$root->appendChild($detalleDesglose); $root->appendChild($detalleDesglose);
return $root; return $root;
@ -291,6 +300,12 @@ class Desglose extends BaseXmlModel
return $this; return $this;
} }
public function addDesgloseIVA(DetalleDesglose $desgloseIVA): self
{
$this->desgloseIVA[] = $desgloseIVA;
return $this;
}
public function getDesgloseIGIC(): ?array public function getDesgloseIGIC(): ?array
{ {
return $this->desgloseIGIC; return $this->desgloseIGIC;

View File

@ -23,11 +23,21 @@ class DetalleDesglose extends BaseXmlModel
// Add IVA details directly under DetalleDesglose // Add IVA details directly under DetalleDesglose
$root->appendChild($this->createElement($doc, 'Impuesto', $this->desgloseIVA['Impuesto'])); $root->appendChild($this->createElement($doc, 'Impuesto', $this->desgloseIVA['Impuesto']));
$root->appendChild($this->createElement($doc, 'ClaveRegimen', $this->desgloseIVA['ClaveRegimen']));
$root->appendChild($this->createElement($doc, 'CalificacionOperacion', $this->desgloseIVA['CalificacionOperacion'] ?? 'S1')); if(isset($this->desgloseIVA['ClaveRegimen']) && in_array($this->desgloseIVA['ClaveRegimen'], ['01','03'])){
$root->appendChild($this->createElement($doc, 'TipoImpositivo', (string)$this->desgloseIVA['TipoImpositivo'])); $root->appendChild($this->createElement($doc, 'ClaveRegimen', $this->desgloseIVA['ClaveRegimen']));
}
$root->appendChild($this->createElement($doc, 'CalificacionOperacion', $this->desgloseIVA['CalificacionOperacion']));
if(isset($this->desgloseIVA['TipoImpositivo']) && $this->desgloseIVA['CalificacionOperacion'] == 'S1') {
$root->appendChild($this->createElement($doc, 'TipoImpositivo', (string)$this->desgloseIVA['TipoImpositivo']));
}
$root->appendChild($this->createElement($doc, 'BaseImponibleOimporteNoSujeto', (string)$this->desgloseIVA['BaseImponible'])); $root->appendChild($this->createElement($doc, 'BaseImponibleOimporteNoSujeto', (string)$this->desgloseIVA['BaseImponible']));
$root->appendChild($this->createElement($doc, 'CuotaRepercutida', (string)$this->desgloseIVA['Cuota']));
if(isset($this->desgloseIVA['Cuota']) && $this->desgloseIVA['CalificacionOperacion'] == 'S1') {
$root->appendChild($this->createElement($doc, 'CuotaRepercutida', (string)$this->desgloseIVA['Cuota']));
}
return $root; return $root;
} }

View File

@ -38,7 +38,7 @@ class IDOtro extends BaseXmlModel
* @param string $id Identifier value, e.g., passport number, tax ID, or placeholder * @param string $id Identifier value, e.g., passport number, tax ID, or placeholder
* @return void * @return void
*/ */
public function __construct(private string $codigoPais = 'ES', private string $idType = '07', private string $id = 'NO_DISPONIBLE') public function __construct(private string $codigoPais = 'ES', private string $idType = '06', private string $id = 'NO_DISPONIBLE')
{ {
} }

View File

@ -27,6 +27,7 @@ use App\Services\EDocument\Standards\Verifactu\Models\IDOtro;
use App\Services\EDocument\Standards\Verifactu\Models\Desglose; use App\Services\EDocument\Standards\Verifactu\Models\Desglose;
use App\Services\EDocument\Standards\Verifactu\Models\IDFactura; use App\Services\EDocument\Standards\Verifactu\Models\IDFactura;
use App\Services\EDocument\Standards\Verifactu\Models\Encadenamiento; use App\Services\EDocument\Standards\Verifactu\Models\Encadenamiento;
use App\Services\EDocument\Standards\Verifactu\Models\DetalleDesglose;
use App\Services\EDocument\Standards\Verifactu\Models\RegistroAnterior; use App\Services\EDocument\Standards\Verifactu\Models\RegistroAnterior;
use App\Services\EDocument\Standards\Verifactu\Models\SistemaInformatico; use App\Services\EDocument\Standards\Verifactu\Models\SistemaInformatico;
use App\Services\EDocument\Standards\Verifactu\Models\PersonaFisicaJuridica; use App\Services\EDocument\Standards\Verifactu\Models\PersonaFisicaJuridica;
@ -58,7 +59,9 @@ class RegistroAlta
'01' => 'IVA (Impuesto sobre el Valor Añadido)', // Value Added Tax - Standard Spanish VAT '01' => 'IVA (Impuesto sobre el Valor Añadido)', // Value Added Tax - Standard Spanish VAT
'02' => 'IPSI (Impuesto sobre la Producción, los Servicios y la Importación)', // Production, Services and Import Tax - Ceuta and Melilla '02' => 'IPSI (Impuesto sobre la Producción, los Servicios y la Importación)', // Production, Services and Import Tax - Ceuta and Melilla
'03' => 'IGIC (Impuesto General Indirecto Canario)', // Canary Islands General Indirect Tax '03' => 'IGIC (Impuesto General Indirecto Canario)', // Canary Islands General Indirect Tax
'05' => 'Otros (Others)' // Other taxes '05' => 'Otros (Others)', // Other taxes
'06' => 'IAE', //local taxes - rarely used
'07' => 'Non-Vat / Exempt operations'
]; ];
private array $clave_regimen_codes = [ private array $clave_regimen_codes = [
@ -141,9 +144,10 @@ class RegistroAlta
->setNombreRazon($this->invoice->client->present()->name()); ->setNombreRazon($this->invoice->client->present()->name());
} }
else { else {
$locationData = $this->invoice->location(); $locationData = $this->invoice->service()->location();
$destinatario = new IDOtro(); $destinatario = new IDOtro();
$destinatario->setNombreRazon($this->invoice->client->present()->name());
$destinatario->setCodigoPais($locationData['country_code']); $destinatario->setCodigoPais($locationData['country_code']);
$br = new \App\DataMapper\Tax\BaseRule(); $br = new \App\DataMapper\Tax\BaseRule();
@ -168,18 +172,60 @@ class RegistroAlta
foreach ($taxes as $tax) { foreach ($taxes as $tax) {
$desglose_iva[] = [ $desglose_iva = [
'Impuesto' => $this->calculateTaxType($tax['name']), //tax type 'Impuesto' => $this->calculateTaxType($tax['name']), //tax type
'ClaveRegimen' => '01', //tax regime classification code 'ClaveRegimen' => $this->calculateRegimeClassification($tax['name']), //tax regime classification code
'CalificacionOperacion' => 'S1', //operation classification code 'CalificacionOperacion' => $this->calculateOperationClassification($tax['name']), //operation classification code
'BaseImponibleOimporteNoSujeto' => $tax['base_amount'] ?? $this->calc->getNetSubtotal(), // taxable base amount 'BaseImponible' => $tax['base_amount'] ?? $this->calc->getNetSubtotal(), // taxable base amount - fixed: key matches DetalleDesglose::toXml()
'TipoImpositivo' => $tax['tax_rate'], // Tax Rate 'TipoImpositivo' => $tax['tax_rate'], // Tax Rate
'CuotaRepercutida' => $tax['total'] // Tax Amount 'Cuota' => $tax['total'] // Tax Amount - fixed: key matches DetalleDesglose::toXml()
]; ];
$detalle_desglose = new DetalleDesglose();
$detalle_desglose->setDesgloseIVA($desglose_iva);
$desglose->addDesgloseIVA($detalle_desglose);
}; };
$desglose->setDesgloseIVA($desglose_iva); if(count($taxes) == 0) {
nlog("tax count = 0");
$client_country_code = $this->invoice->client->country->iso_3166_2;
$impuesto = 'S2';
$clave_regimen = '08';
$calificacion = 'S1';
$br = new \App\DataMapper\Tax\BaseRule();
if (in_array($client_country_code, $br->eu_country_codes) && $this->invoice->client->classification != 'individual') {
$impuesto = '05';
$clave_regimen = '05';
$calificacion = 'N2';
}
elseif (in_array($client_country_code, $br->eu_country_codes) && $this->invoice->client->classification == 'individual') {
$impuesto = '08';
$clave_regimen = '05';
$calificacion = 'N2';
}
else{
$impuesto = '08';
$clave_regimen = '01';
$calificacion = 'N2';
}
$desglose_iva = [
'Impuesto' => $impuesto, //tax type
'ClaveRegimen' => $clave_regimen, //tax regime classification code
'CalificacionOperacion' => $calificacion, //operation classification code
'BaseImponible' => $this->calc->getNetSubtotal(), // taxable base amount - fixed: key matches DetalleDesglose::toXml()
];
$detalle_desglose = new DetalleDesglose();
$detalle_desglose->setDesgloseIVA($desglose_iva);
$desglose->addDesgloseIVA($detalle_desglose);
}
$this->v_invoice->setDesglose($desglose); $this->v_invoice->setDesglose($desglose);
@ -261,25 +307,85 @@ class RegistroAlta
return $this->v_invoice; return $this->v_invoice;
} }
private function calculateTaxType(string $tax_name): string private function calculateRegimeClassification(string $tax_name): string
{ {
if(stripos($tax_name, 'iva') !== false) { $client_country_code = $this->invoice->client->country->iso_3166_2;
if($client_country_code == 'ES') {
if(stripos($tax_name, 'iva') !== false) {
return '01';
}
if(stripos($tax_name, 'igic') !== false) {
return '03';
}
if(stripos($tax_name, 'ipsi') !== false) {
return '02';
}
if(stripos($tax_name, 'otros') !== false) {
return '05';
}
return '01'; return '01';
} }
if(stripos($tax_name, 'igic') !== false) { $br = new \App\DataMapper\Tax\BaseRule();
return '03'; if (in_array($client_country_code, $br->eu_country_codes) && $this->invoice->client->classification != 'individual') {
} return '08';
} elseif (in_array($client_country_code, $br->eu_country_codes) && $this->invoice->client->classification == 'individual') {
if(stripos($tax_name, 'ipsi') !== false) {
return '02';
}
if(stripos($tax_name, 'otros') !== false) {
return '05'; return '05';
} }
return '01'; return '07';
}
private function calculateTaxType(string $tax_name): string
{
$client_country_code = $this->invoice->client->country->iso_3166_2;
if($client_country_code == 'ES') {
if(stripos($tax_name, 'iva') !== false) {
return '01';
}
if(stripos($tax_name, 'igic') !== false) {
return '03';
}
if(stripos($tax_name, 'ipsi') !== false) {
return '02';
}
if(stripos($tax_name, 'otros') !== false) {
return '05';
}
return '01';
}
$br = new \App\DataMapper\Tax\BaseRule();
if (in_array($client_country_code, $br->eu_country_codes) && $this->invoice->client->classification != 'individual') {
return '08';
}
elseif (in_array($client_country_code, $br->eu_country_codes) && $this->invoice->client->classification == 'individual') {
return '05';
}
return '07';
}
private function calculateOperationClassification(string $tax_name): string
{
if($this->invoice->client->country_id == 724 || stripos($tax_name, 'iva') !== false) {
return 'S1';
}
return 'N2';
} }
} }

View File

@ -127,7 +127,7 @@ class SendToAeat implements ShouldQueue
$verifactu = new Verifactu($invoice); $verifactu = new Verifactu($invoice);
$document = (new RegistroAlta($invoice))->run()->getInvoice(); $document = (new RegistroAlta($invoice))->run()->getInvoice();
$document->setNumSerieFactura($invoice->backup->parent_invoice_number);
$last_hash = $invoice->company->verifactu_logs()->first(); $last_hash = $invoice->company->verifactu_logs()->first();
$huella = $this->cancellationHash($document, $last_hash->hash); $huella = $this->cancellationHash($document, $last_hash->hash);

View File

@ -714,6 +714,7 @@ class InvoiceService
if($new_model && $this->invoice->amount >= 0) { if($new_model && $this->invoice->amount >= 0) {
$this->invoice->backup->document_type = 'F1'; $this->invoice->backup->document_type = 'F1';
$this->invoice->backup->adjustable_amount = $this->invoice->amount; $this->invoice->backup->adjustable_amount = $this->invoice->amount;
$this->invoice->backup->parent_invoice_number = $this->invoice->number;
$this->invoice->saveQuietly(); $this->invoice->saveQuietly();
} }
elseif(isset($invoice_array['modified_invoice_id'])) { elseif(isset($invoice_array['modified_invoice_id'])) {
@ -726,6 +727,7 @@ class InvoiceService
//Update the client balance by the delta amount from the previous invoice to this one. //Update the client balance by the delta amount from the previous invoice to this one.
$this->invoice->backup->parent_invoice_id = $modified_invoice->hashed_id; $this->invoice->backup->parent_invoice_id = $modified_invoice->hashed_id;
$this->invoice->backup->document_type = 'R2'; $this->invoice->backup->document_type = 'R2';
$this->invoice->backup->parent_invoice_number = $modified_invoice->number;
$this->invoice->saveQuietly(); $this->invoice->saveQuietly();
$this->invoice->client->service()->updateBalance(round(($this->invoice->amount - $modified_invoice->amount), 2)); $this->invoice->client->service()->updateBalance(round(($this->invoice->amount - $modified_invoice->amount), 2));

View File

@ -125,6 +125,7 @@
"friendsofphp/php-cs-fixer": "^3.14", "friendsofphp/php-cs-fixer": "^3.14",
"laracasts/cypress": "^3.0", "laracasts/cypress": "^3.0",
"larastan/larastan": "^2", "larastan/larastan": "^2",
"laravel/boost": "^1.0",
"mockery/mockery": "^1.4.4", "mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^8.1", "nunomaduro/collision": "^8.1",
"phpstan/phpstan": "^1.9", "phpstan/phpstan": "^1.9",

192
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "df5805c56ea07b9fe22be83885c67387", "content-hash": "a6ef0e9ae03d968020a5c8a976835ad9",
"packages": [ "packages": [
{ {
"name": "adrienrn/php-mimetyper", "name": "adrienrn/php-mimetyper",
@ -18381,6 +18381,196 @@
], ],
"time": "2025-06-10T22:06:33+00:00" "time": "2025-06-10T22:06:33+00:00"
}, },
{
"name": "laravel/boost",
"version": "v1.0.9",
"source": {
"type": "git",
"url": "https://github.com/laravel/boost.git",
"reference": "ad025a2f2325ccdc5f52e926dab4e0c1d2c6def9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/boost/zipball/ad025a2f2325ccdc5f52e926dab4e0c1d2c6def9",
"reference": "ad025a2f2325ccdc5f52e926dab4e0c1d2c6def9",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^7.9",
"illuminate/console": "^10.0|^11.0|^12.0",
"illuminate/contracts": "^10.0|^11.0|^12.0",
"illuminate/routing": "^10.0|^11.0|^12.0",
"illuminate/support": "^10.0|^11.0|^12.0",
"laravel/mcp": "^0.1.0",
"laravel/prompts": "^0.1.9|^0.3",
"laravel/roster": "^0.2",
"php": "^8.1|^8.2"
},
"require-dev": {
"laravel/pint": "^1.14|^1.23",
"mockery/mockery": "^1.6",
"orchestra/testbench": "^8.22.0|^9.0|^10.0",
"pestphp/pest": "^2.0|^3.0",
"phpstan/phpstan": "^2.0"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Laravel\\Boost\\BoostServiceProvider"
]
},
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Laravel\\Boost\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Laravel Boost accelerates AI-assisted development to generate high-quality, Laravel-specific code.",
"homepage": "https://github.com/laravel/boost",
"keywords": [
"ai",
"dev",
"laravel"
],
"support": {
"issues": "https://github.com/laravel/boost/issues",
"source": "https://github.com/laravel/boost"
},
"time": "2025-08-13T19:51:44+00:00"
},
{
"name": "laravel/mcp",
"version": "v0.1.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/mcp.git",
"reference": "417890c0d8032af9a46a86d16651bbe13946cddf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/mcp/zipball/417890c0d8032af9a46a86d16651bbe13946cddf",
"reference": "417890c0d8032af9a46a86d16651bbe13946cddf",
"shasum": ""
},
"require": {
"illuminate/console": "^10.0|^11.0|^12.0",
"illuminate/contracts": "^10.0|^11.0|^12.0",
"illuminate/http": "^10.0|^11.0|^12.0",
"illuminate/routing": "^10.0|^11.0|^12.0",
"illuminate/support": "^10.0|^11.0|^12.0",
"illuminate/validation": "^10.0|^11.0|^12.0",
"php": "^8.1|^8.2"
},
"require-dev": {
"laravel/pint": "^1.14",
"orchestra/testbench": "^8.22.0|^9.0|^10.0",
"phpstan/phpstan": "^2.0"
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"Mcp": "Laravel\\Mcp\\Server\\Facades\\Mcp"
},
"providers": [
"Laravel\\Mcp\\Server\\McpServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Laravel\\Mcp\\": "src/",
"Workbench\\App\\": "workbench/app/",
"Laravel\\Mcp\\Tests\\": "tests/",
"Laravel\\Mcp\\Server\\": "src/Server/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "The easiest way to add MCP servers to your Laravel app.",
"homepage": "https://github.com/laravel/mcp",
"keywords": [
"dev",
"laravel",
"mcp"
],
"support": {
"issues": "https://github.com/laravel/mcp/issues",
"source": "https://github.com/laravel/mcp"
},
"time": "2025-08-12T07:09:39+00:00"
},
{
"name": "laravel/roster",
"version": "v0.2.3",
"source": {
"type": "git",
"url": "https://github.com/laravel/roster.git",
"reference": "caeed7609b02c00c3f1efec52812d8d87c5d4096"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/roster/zipball/caeed7609b02c00c3f1efec52812d8d87c5d4096",
"reference": "caeed7609b02c00c3f1efec52812d8d87c5d4096",
"shasum": ""
},
"require": {
"illuminate/console": "^10.0|^11.0|^12.0",
"illuminate/contracts": "^10.0|^11.0|^12.0",
"illuminate/routing": "^10.0|^11.0|^12.0",
"illuminate/support": "^10.0|^11.0|^12.0",
"php": "^8.1|^8.2",
"symfony/yaml": "^6.4|^7.2"
},
"require-dev": {
"laravel/pint": "^1.14",
"mockery/mockery": "^1.6",
"orchestra/testbench": "^8.22.0|^9.0|^10.0",
"pestphp/pest": "^2.0|^3.0",
"phpstan/phpstan": "^2.0"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Laravel\\Roster\\RosterServiceProvider"
]
},
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Laravel\\Roster\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Detect packages & approaches in use within a Laravel project",
"homepage": "https://github.com/laravel/roster",
"keywords": [
"dev",
"laravel"
],
"support": {
"issues": "https://github.com/laravel/roster/issues",
"source": "https://github.com/laravel/roster"
},
"time": "2025-08-13T15:00:25+00:00"
},
{ {
"name": "mockery/mockery", "name": "mockery/mockery",
"version": "1.6.12", "version": "1.6.12",