diff --git a/app/Services/Invoice/EInvoice/FacturaEInvoice.php b/app/Services/Invoice/EInvoice/FacturaEInvoice.php index cd6a3e5bb75a..5a2bcce7c35b 100644 --- a/app/Services/Invoice/EInvoice/FacturaEInvoice.php +++ b/app/Services/Invoice/EInvoice/FacturaEInvoice.php @@ -212,6 +212,9 @@ class FacturaEInvoice extends AbstractService } + if(count($data) == 0) + $data[Facturae::TAX_IVA] = 0; + return $data; } @@ -258,29 +261,30 @@ class FacturaEInvoice extends AbstractService $company = $this->invoice->company; $seller = new FacturaeParty([ - "isLegalEntity" => true, // Se asume true si se omite - "taxNumber" => $company->settings->vat_number, - "name" => $company->present()->name(), - "address" => $company->settings->address1, - "postCode" => $company->settings->postal_code, - "town" => $company->settings->city, - "province" => $company->settings->state, - "countryCode" => $company->country()->iso_3166_3, // Se asume España si se omite - "book" => "0", // Libro - "merchantRegister" => "RG", // Registro Mercantil - "sheet" => "1", // Hoja - "folio" => "2", // Folio - "section" => "3", // Sección - "volume" => "4", // Tomo - "email" => $company->settings->email, - "phone" => $company->settings->phone, - "fax" => "", - "website" => $company->settings->website, - "contactPeople" => $company->owner()->present()->name(), - // "cnoCnae" => "04647", // Clasif. Nacional de Act. Económicas - // "ineTownCode" => "280796" // Cód. de municipio del INE + "isLegalEntity" => true, // Se asume true si se omite + "taxNumber" => $company->settings->vat_number, + "name" => substr($company->present()->name(), 0, 40), + "address" => substr($company->settings->address1, 0, 80), + "postCode" => substr($this->invoice->client->postal_code, 0, 5), + "town" => substr($company->settings->city, 0, 50), + "province" => substr($company->settings->state, 0, 20), + "countryCode" => $company->country()->iso_3166_3, // Se asume España si se omite + "book" => "0", // Libro + "merchantRegister" => "RG", // Registro Mercantil + "sheet" => "1", // Hoja + "folio" => "2", // Folio + "section" => "3", // Sección + "volume" => "4", // Tomo + "email" => substr($company->settings->email, 0, 60), + "phone" => substr($company->settings->phone, 0, 15), + "fax" => "", + "website" => substr($company->settings->website, 0, 50), + "contactPeople" => substr($company->owner()->present()->name(), 0, 40), + // "cnoCnae" => "04647", // Clasif. Nacional de Act. Económicas + // "ineTownCode" => "280796" // Cód. de municipio del INE ]); + $this->fac->setSeller($seller); return $this; @@ -292,19 +296,19 @@ class FacturaEInvoice extends AbstractService $buyer = new FacturaeParty([ "isLegalEntity" => $this->invoice->client->has_valid_vat_number, "taxNumber" => $this->invoice->client->vat_number, - "name" => $this->invoice->client->present()->name(), - "firstSurname" => $this->invoice->client->present()->first_name(), - "lastSurname" => $this->invoice->client->present()->last_name(), - "address" => $this->invoice->client->address1, - "postCode" => $this->invoice->client->postal_code, - "town" => $this->invoice->client->city, - "province" => $this->invoice->client->state, + "name" => substr($this->invoice->client->present()->name(),0, 40), + "firstSurname" => substr($this->invoice->client->present()->first_name(),0, 40), + "lastSurname" => substr($this->invoice->client->present()->last_name(),0, 40), + "address" => substr($this->invoice->client->address1,0, 80), + "postCode" => substr($this->invoice->client->postal_code,0,5), + "town" => substr($this->invoice->client->city,0, 50), + "province" => substr($this->invoice->client->state,0, 20), "countryCode" => $this->invoice->client->country->iso_3166_3, // Se asume España si se omite - "email" => $this->invoice->client->present()->email(), - "phone" => $this->invoice->client->present()->phone(), + "email" => substr($this->invoice->client->present()->email(),0, 60), + "phone" => substr($this->invoice->client->present()->phone(),0, 15), "fax" => "", "website" => substr($this->invoice->client->present()->website(), 0 ,60), - "contactPeople" => $this->invoice->client->present()->first_name()." ".$this->invoice->client->present()->last_name(), + "contactPeople" => substr($this->invoice->client->present()->first_name()." ".$this->invoice->client->present()->last_name(), 0, 40), // "cnoCnae" => "04791", // Clasif. Nacional de Act. Económicas // "ineTownCode" => "280796" // Cód. de municipio del INE ]); diff --git a/openapi/api-docs.yaml b/openapi/api-docs.yaml index b875dd9f0716..1e96c319fc02 100644 --- a/openapi/api-docs.yaml +++ b/openapi/api-docs.yaml @@ -16907,9 +16907,10 @@ components: type: number example: 8.00 line_items: - description: "Array of line items included in the invoice" - type: object - example: "[{item1}, {item2}]" + type: array + description: 'An array of objects which define the line items of the invoice' + items: + $ref: '#/components/schemas/InvoiceItem' discount: description: "The discount applied to the invoice" type: number @@ -17324,83 +17325,116 @@ components: quantity: type: integer example: 1 + description: 'The quantity of the product offered for this line item' cost: type: number format: float example: 10.00 + description: 'The cost of the product offered for this line item' product_key: type: string example: 'Product key' + description: 'The product key of the product offered for this line item (Referred to as Product in the product tab)' product_cost: type: number format: float example: 10.00 + description: 'The cost of the product offered for this line item (Referred to as Cost in the product tab)' notes: type: string example: 'Item notes' + description: 'The notes/description for the product offered for this line item' discount: type: number format: float example: 5.00 + description: 'The discount applied to the product offered for this line item' is_amount_discount: type: boolean example: false + description: 'Indicates whether the discount applied to the product offered for this line item is a fixed amount or a percentage' tax_name1: type: string - example: 'Tax name 1' + example: 'GST' + description: 'The name of the first tax applied to the product offered for this line item' tax_rate1: type: number format: float example: 10.00 + description: 'The rate of the first tax applied to the product offered for this line item' tax_name2: type: string - example: 'Tax name 2' + example: 'VAT' + description: 'The name of the second tax applied to the product offered for this line item' tax_rate2: type: number format: float example: 5.00 + description: 'The rate of the second tax applied to the product offered for this line item' tax_name3: type: string - example: 'Tax name 3' + example: 'CA Sales Tax' + description: 'The name of the third tax applied to the product offered for this line item' tax_rate3: type: number format: float example: 3.00 + description: 'The rate of the third tax applied to the product offered for this line item' sort_id: type: string example: '0' + description: 'Deprecated' + deprecated: true line_total: type: number format: float example: 10.00 + description: 'The total amount of the product offered for this line item' + readOnly: true gross_line_total: type: number format: float example: 15.00 + description: 'The total amount of the product offered for this line item before discounts' + readOnly: true tax_amount: type: number format: float example: 1.00 + description: 'The total amount of tax applied to the product offered for this line item' + readOnly: true date: type: string format: date-time example: '2023-03-19T00:00:00Z' + description: 'Deprecated' + deprecated: true custom_value1: type: string example: 'Custom value 1' + description: 'The first custom value of the product offered for this line item' custom_value2: type: string example: 'Custom value 2' + description: 'The second custom value of the product offered for this line item' custom_value3: type: string example: 'Custom value 3' + description: 'The third custom value of the product offered for this line item' custom_value4: type: string example: 'Custom value 4' + description: 'The fourth custom value of the product offered for this line item' type_id: type: string example: '1' description: '1 = product, 2 = service, 3 unpaid gateway fee, 4 paid gateway fee, 5 late fee, 6 expense' + default: '1' + tax_id: + type: string + example: '1' + default: '1' + description: 'The tax ID of the product: 1 product, 2 service, 3 digital, 4 shipping, 5 exempt, 5 reduced tax, 7 override, 8 zero rate, 9 reverse tax' CompanyUser: properties: permissions: @@ -19431,17 +19465,17 @@ components: cost: type: number format: double - description: 'The cost of the product.' + description: 'The cost of the product. (Your purchase price for this product)' example: 10.0 price: type: number format: double - description: 'The price of the product.' + description: 'The price of the product that you are charging.' example: 20.0 quantity: type: number format: double - description: 'The quantity of the product.' + description: 'The quantity of the product. (used as a default)' example: 5.0 tax_name1: type: string diff --git a/openapi/components/schemas/fillable_invoice.yaml b/openapi/components/schemas/fillable_invoice.yaml index d6593eefe1fe..2ae598a460bc 100644 --- a/openapi/components/schemas/fillable_invoice.yaml +++ b/openapi/components/schemas/fillable_invoice.yaml @@ -73,9 +73,10 @@ type: number example: 8.00 line_items: - description: "Array of line items included in the invoice" - type: object - example: "[{item1}, {item2}]" + type: array + description: 'An array of objects which define the line items of the invoice' + items: + $ref: '#/components/schemas/InvoiceItem' discount: description: "The discount applied to the invoice" type: number diff --git a/openapi/components/schemas/invoice_item.yaml b/openapi/components/schemas/invoice_item.yaml index 94c512f49e1b..2357c127c526 100644 --- a/openapi/components/schemas/invoice_item.yaml +++ b/openapi/components/schemas/invoice_item.yaml @@ -4,80 +4,113 @@ quantity: type: integer example: 1 + description: 'The quantity of the product offered for this line item' cost: type: number format: float example: 10.00 + description: 'The cost of the product offered for this line item' product_key: type: string example: 'Product key' + description: 'The product key of the product offered for this line item (Referred to as Product in the product tab)' product_cost: type: number format: float example: 10.00 + description: 'The cost of the product offered for this line item (Referred to as Cost in the product tab)' notes: type: string example: 'Item notes' + description: 'The notes/description for the product offered for this line item' discount: type: number format: float example: 5.00 + description: 'The discount applied to the product offered for this line item' is_amount_discount: type: boolean example: false + description: 'Indicates whether the discount applied to the product offered for this line item is a fixed amount or a percentage' tax_name1: type: string - example: 'Tax name 1' + example: 'GST' + description: 'The name of the first tax applied to the product offered for this line item' tax_rate1: type: number format: float example: 10.00 + description: 'The rate of the first tax applied to the product offered for this line item' tax_name2: type: string - example: 'Tax name 2' + example: 'VAT' + description: 'The name of the second tax applied to the product offered for this line item' tax_rate2: type: number format: float example: 5.00 + description: 'The rate of the second tax applied to the product offered for this line item' tax_name3: type: string - example: 'Tax name 3' + example: 'CA Sales Tax' + description: 'The name of the third tax applied to the product offered for this line item' tax_rate3: type: number format: float example: 3.00 + description: 'The rate of the third tax applied to the product offered for this line item' sort_id: type: string example: '0' + description: 'Deprecated' + deprecated: true line_total: type: number format: float example: 10.00 + description: 'The total amount of the product offered for this line item' + readOnly: true gross_line_total: type: number format: float example: 15.00 + description: 'The total amount of the product offered for this line item before discounts' + readOnly: true tax_amount: type: number format: float example: 1.00 + description: 'The total amount of tax applied to the product offered for this line item' + readOnly: true date: type: string format: date-time example: '2023-03-19T00:00:00Z' + description: 'Deprecated' + deprecated: true custom_value1: type: string example: 'Custom value 1' + description: 'The first custom value of the product offered for this line item' custom_value2: type: string example: 'Custom value 2' + description: 'The second custom value of the product offered for this line item' custom_value3: type: string example: 'Custom value 3' + description: 'The third custom value of the product offered for this line item' custom_value4: type: string example: 'Custom value 4' + description: 'The fourth custom value of the product offered for this line item' type_id: type: string example: '1' - description: '1 = product, 2 = service, 3 unpaid gateway fee, 4 paid gateway fee, 5 late fee, 6 expense' \ No newline at end of file + description: '1 = product, 2 = service, 3 unpaid gateway fee, 4 paid gateway fee, 5 late fee, 6 expense' + default: '1' + tax_id: + type: string + example: '1' + default: '1' + description: 'The tax ID of the product: 1 product, 2 service, 3 digital, 4 shipping, 5 exempt, 5 reduced tax, 7 override, 8 zero rate, 9 reverse tax' \ No newline at end of file diff --git a/openapi/components/schemas/product.yaml b/openapi/components/schemas/product.yaml index a88238e392a8..52480f71978b 100644 --- a/openapi/components/schemas/product.yaml +++ b/openapi/components/schemas/product.yaml @@ -55,17 +55,17 @@ cost: type: number format: double - description: 'The cost of the product.' + description: 'The cost of the product. (Your purchase price for this product)' example: 10.0 price: type: number format: double - description: 'The price of the product.' + description: 'The price of the product that you are charging.' example: 20.0 quantity: type: number format: double - description: 'The quantity of the product.' + description: 'The quantity of the product. (used as a default)' example: 5.0 tax_name1: type: string diff --git a/tests/Feature/EInvoice/FacturaeTest.php b/tests/Feature/EInvoice/FacturaeTest.php index 77da9477a011..06d6f2b05a1b 100644 --- a/tests/Feature/EInvoice/FacturaeTest.php +++ b/tests/Feature/EInvoice/FacturaeTest.php @@ -13,8 +13,6 @@ namespace Tests\Feature\EInvoice; use Tests\TestCase; use Tests\MockAccountData; -use Http\Message\CookieJar; -use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Storage; use App\Services\Invoice\EInvoice\FacturaEInvoice; use Illuminate\Routing\Middleware\ThrottleRequests; @@ -51,29 +49,89 @@ class FacturaeTest extends TestCase nlog($f->run()); - // $this->assertTrue($this->validateInvoiceXML($path)); + $this->assertTrue($this->validateInvoiceXML($path)); } -// private function validateInvoiceXML($path) { +// protected function validateInvoiceXML($path, $validateSignature=false) { +// // Prepare file to upload +// if (function_exists('curl_file_create')) { +// $postFile = curl_file_create($path); +// } else { +// $postFile = "@" . realpath($path); +// } + +// // Send upload request +// $ch = curl_init(); +// curl_setopt_array($ch, array( +// CURLOPT_RETURNTRANSFER => true, +// CURLOPT_FOLLOWLOCATION => true, +// CURLOPT_URL => "http://plataforma.firma-e.com/VisualizadorFacturae/index2.jsp", +// CURLOPT_POST => 1, +// CURLOPT_POSTFIELDS => array( +// "referencia" => $postFile, +// "valContable" => "on", +// "valFirma" => $validateSignature ? "on" : "off", +// "aceptarCondiciones" => "on", +// "submit" => "Siguiente" +// ), +// CURLOPT_COOKIEJAR => base_path()."/cookie.txt" +// )); +// $res = curl_exec($ch); +// curl_close($ch); +// unset($ch); + +// nlog($res); + +// if (strpos($res, "window.open('facturae.jsp'") === false) { +// $this->expectException(\UnexpectedValueException::class); +// } + +// // Fetch results +// $ch = curl_init(); +// curl_setopt_array($ch, array( +// CURLOPT_RETURNTRANSFER => true, +// CURLOPT_FOLLOWLOCATION => true, +// CURLOPT_URL => "http://plataforma.firma-e.com/VisualizadorFacturae/facturae.jsp", +// CURLOPT_COOKIEFILE => base_path()."/cookie.txt" +// )); +// $res = curl_exec($ch); +// curl_close($ch); +// unset($ch); + +// nlog($res); + +// // Validate results +// $this->assertNotEmpty($res, 'Invalid Validator Response'); +// $this->assertNotEmpty(strpos($res, 'euro_ok.png'), 'Invalid XML Format'); +// if ($validateSignature) { +// $this->assertNotEmpty(strpos($res, '>Nivel de Firma Válido<'), 'Invalid Signature'); +// } +// if (strpos($res, '>Sellos de Tiempo<') !== false) { +// $this->assertNotEmpty(strpos($res, '>XAdES_T<'), 'Invalid Timestamp'); +// } +// } + + // private function validateInvoiceXML($path) + // { + // $client = new \GuzzleHttp\Client(['cookies' => true]); + + // $response = $client->request('POST', 'https://face.gob.es/api/v1/herramientas/validador',[ + // 'multipart' => [ + // [ + // 'name' => 'validador[factura]', + // 'contents' => Storage::get($path), + // ], + // ] + // ]); + + // $response = $client->request('POST', 'http://plataforma.firma-e.com/VisualizadorFacturae/facturae.jsp'); + // $body = $response->getBody(); + // $stringBody = (string) $body; + + // echo print_r($stringBody,1); - -// $jar = (new \GuzzleHttp\Cookie\CookieJar())->toArray(); - -// echo print_r($jar); - -// $response = Http::withCookies($jar, '.ninja.test')->attach( -// 'xmlFile', -// Storage::get($path), -// basename($path) -// )->post('https://viewer.facturadirecta.com/dp/viewer/upload.void'); // Instance of Guzzle/CookieJar - -// echo print_r($jar); - -// $response = Http::withCookies($jar, '.ninja.test')->post('https://viewer.facturadirecta.com/dp/viewer/viewer.void'); -// echo print_r($response->body(), 1); - -// } + // } } \ No newline at end of file