Static analysis cleanup

This commit is contained in:
David Bomba 2025-08-08 15:12:24 +10:00
parent 1a86d5445b
commit 3791469c31
30 changed files with 186 additions and 256 deletions

View File

@ -39,6 +39,7 @@ use Laracasts\Presenter\PresentableTrait;
* @property string|null $plan_expires
* @property string|null $user_agent
* @property string|null $key
* @property string|null $e_invoice_token
* @property int|null $payment_id
* @property int $default_company_id
* @property string|null $trial_started
@ -71,6 +72,7 @@ use Laracasts\Presenter\PresentableTrait;
* @property string|null $account_sms_verification_number
* @property bool $account_sms_verified
* @property string|null $bank_integration_account_id
* @property string|null $e_invoicing_token
* @property bool $is_trial
* @property int $e_invoice_quota
* @property-read int|null $bank_integrations_count

View File

@ -37,6 +37,7 @@ use Illuminate\Contracts\Translation\HasLocalePreference;
* @property int $id
* @property int $company_id
* @property int $user_id
* @property int|null $location_id
* @property int|null $assigned_user_id
* @property string|null $name
* @property string|null $website
@ -86,7 +87,11 @@ use Illuminate\Contracts\Translation\HasLocalePreference;
* @property-read \App\Models\User $user
* @property-read \App\Models\Company $company
* @property-read \App\Models\Country|null $country
* @property-read \App\Models\Country|null $shipping_country
* @property-read \App\Models\Industry|null $industry
* @property-read \App\Models\Size|null $size
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Location> $locations
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\CompanyLedger> $company_ledger
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ClientContact> $contacts
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Credit> $credits

View File

@ -93,6 +93,7 @@ use Laracasts\Presenter\PresentableTrait;
* @property bool $markdown_enabled
* @property bool $use_comma_as_decimal_place
* @property bool $report_include_drafts
* @property bool $invoice_task_project_header
* @property array|null $client_registration_fields
* @property bool $convert_rate_to_client
* @property bool $markdown_email_enabled
@ -129,11 +130,15 @@ use Laracasts\Presenter\PresentableTrait;
* @property int|null $smtp_port
* @property string|null $smtp_encryption
* @property string|null $smtp_local_domain
* @property boolean $invoice_task_item_description
* @property \App\DataMapper\QuickbooksSettings|null $quickbooks
* @property boolean $smtp_verify_peer
* @property object|null $origin_tax_data
* @property int|null $legal_entity_id
* @property-read \App\Models\Account $account
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Location> $locations
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VerifactuLog> $verifactu_logs
* @property-read int|null $activities_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $all_activities
* @property-read int|null $all_activities_count

View File

@ -91,6 +91,9 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property string|null $reminder2_sent
* @property string|null $reminder3_sent
* @property string|null $reminder_last_sent
* @property object|null $tax_data
* @property object|null $e_invoice
* @property int|null $location_id
* @property float $paid_to_date
* @property int|null $subscription_id
* @property \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
@ -118,6 +121,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property \App\Models\Client $client
* @property \App\Models\Vendor|null $vendor
* @property-read mixed $pivot
* @property-read \App\Models\Location|null $location
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\CompanyLedger> $company_ledger
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents

View File

@ -26,6 +26,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property object|null $design
* @property bool $is_deleted
* @property bool $is_template
* @property string|null $entities
* @property int|null $created_at
* @property int|null $updated_at
* @property int|null $deleted_at

View File

@ -74,6 +74,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property-read int|null $documents_count
* @property-read mixed $hashed_id
* @property-read \App\Models\PaymentType|null $payment_type
* @property-read \App\Models\Currency|null $invoice_currency
* @property-read \App\Models\Project|null $project
* @property-read \App\Models\PurchaseOrder|null $purchase_order
* @property-read \App\Models\User $user

View File

@ -38,6 +38,7 @@ use App\Utils\Number;
* @property object|null $e_invoice
* @property int $client_id
* @property int $user_id
* @property int|null $location_id
* @property int|null $assigned_user_id
* @property int $company_id
* @property int $status_id
@ -125,6 +126,9 @@ use App\Utils\Number;
* @property-read int|null $tasks_count
* @property-read \App\Models\User $user
* @property-read \App\Models\Vendor|null $vendor
* @property-read \App\Models\Location|null $location
* @property-read \App\Models\Quote|null $quote
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VerifactuLog> $verifactu_logs
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\CompanyLedger> $company_ledger
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Credit> $credits

View File

@ -22,6 +22,8 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property int $id
* @property int $company_id
* @property int $user_id
* @property int $client_id
* @property int $vendor_id
* @property int|null $assigned_user_id
* @property string|null $name
* @property string|null $website

View File

@ -31,6 +31,7 @@ use Laracasts\Presenter\PresentableTrait;
* @property bool $is_deleted
* @property string|null $number
* @property string $color
* @property int|null $current_hours
* @property-read \App\Models\Client|null $client
* @property-read \App\Models\Company $company
* @property-read int|null $documents_count
@ -53,6 +54,10 @@ use Laracasts\Presenter\PresentableTrait;
* @method static \Illuminate\Database\Eloquent\Builder|Project withoutTrashed()
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Task> $tasks
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Invoice> $invoices
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Quote> $quotes
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Expense> $expenses
*
* @mixin \Eloquent
*/
class Project extends BaseModel

View File

@ -59,6 +59,7 @@ use App\Events\PurchaseOrder\PurchaseOrderWasEmailed;
* @property float $tax_rate3
* @property float $total_taxes
* @property bool $uses_inclusive_taxes
* @property int|null $location_id
* @property string|null $reminder1_sent
* @property string|null $reminder2_sent
* @property string|null $reminder3_sent
@ -100,6 +101,10 @@ use App\Events\PurchaseOrder\PurchaseOrderWasEmailed;
* @property \App\Models\User $user
* @property \App\Models\Vendor $vendor
* @property \App\Models\PurchaseOrderInvitation $invitation
* @property \App\Models\Currency|null $currency
* @property \App\Models\Location|null $location
* @property object|null $tax_data
* @property object|null $e_invoice
* @method static \Illuminate\Database\Eloquent\Builder|PurchaseOrder exclude($columns)
* @method static \Database\Factories\PurchaseOrderFactory factory($count = null, $state = [])
* @method static \Illuminate\Database\Eloquent\Builder|PurchaseOrder filter(\App\Filters\QueryFilters $filters)

View File

@ -81,6 +81,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property int $custom_surcharge_tax2
* @property int $custom_surcharge_tax3
* @property int $custom_surcharge_tax4
* @property int|null $location_id
* @property float $exchange_rate
* @property float $amount
* @property float $balance
@ -94,6 +95,9 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property string|null $reminder2_sent
* @property string|null $reminder3_sent
* @property string|null $reminder_last_sent
* @property int|null $location_id
* @property object|null $tax_data
* @property object|null $e_invoice
* @property float $paid_to_date
* @property int|null $subscription_id
* @property \App\Models\User|null $assigned_user
@ -110,6 +114,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property-read \App\Models\Project|null $project
* @property-read \App\Models\User $user
* @property-read \App\Models\Vendor|null $vendor
* @property-read \App\Models\Location|null $location
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Backup> $history

View File

@ -83,6 +83,7 @@ use App\Models\Presenters\RecurringInvoicePresenter;
* @property bool $custom_surcharge_tax3
* @property bool $custom_surcharge_tax4
* @property string|null $due_date_days
* @property int|null $location_id
* @property string|null $partial_due_date
* @property float $exchange_rate
* @property float $paid_to_date
@ -108,6 +109,7 @@ use App\Models\Presenters\RecurringInvoicePresenter;
* @property-read \App\Models\Subscription|null $subscription
* @property-read \App\Models\User $user
* @property-read \App\Models\Vendor|null $vendor
* @property-read \App\Models\Location|null $location
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns)
* @method static \Database\Factories\RecurringInvoiceFactory factory($count = null, $state = [])
* @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice filter(\App\Filters\QueryFilters $filters)

View File

@ -57,6 +57,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property int $use_inventory_management
* @property string|null $optional_product_ids
* @property string|null $optional_recurring_product_ids
* @property string|null $steps
* @property-read \App\Models\Company $company
* @property-read mixed $hashed_id
* @property-read \App\Models\GroupSetting|null $group_settings

View File

@ -54,6 +54,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property string|null $custom_value4
* @property string|null $vendor_hash
* @property string|null $public_notes
* @property string|null $classification
* @property string|null $id_number
* @property int|null $language_id
* @property int|null $last_login
@ -71,6 +72,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact
* @property-read int|null $primary_contact_count
* @property-read \App\Models\User $user
* @property-read \App\Models\Language|null $language
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns)
* @method static \Database\Factories\VendorFactory factory($count = null, $state = [])
* @method static \Illuminate\Database\Eloquent\Builder|Vendor filter(\App\Filters\QueryFilters $filters)
@ -85,6 +87,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $contacts
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Location> $locations
* @mixin \Eloquent
*/
class Vendor extends BaseModel

View File

@ -1,9 +1,19 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Models;
use App\Models\Company;
use Illuminate\Database\Eloquent\Model;
use App\Services\EDocument\Standards\Verifactu\Models\Invoice;
use App\Models\Invoice;
/**
* @property int $id
@ -43,6 +53,6 @@ class VerifactuLog extends Model
public function deserialize()
{
return Invoice::unserialize($this->state);
return \App\Services\EDocument\Standards\Verifactu\Models\Invoice::unserialize($this->state);
}
}

View File

@ -74,40 +74,10 @@ class InvoiceValidator
$errors[] = "Invalid FechaHoraHusoGenRegistro format. Expected: YYYY-MM-DDTHH:MM:SS+HH:MM, Got: {$fechaHora}";
}
// Validate FechaExpedicionFactura format (YYYY-MM-DD)
if ($invoice->getIdFactura() && method_exists($invoice->getIdFactura(), 'getFechaExpedicionFactura')) {
$fecha = $invoice->getIdFactura()->getFechaExpedicionFactura();
if ($fecha && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $fecha)) {
$errors[] = "Invalid FechaExpedicionFactura format. Expected: YYYY-MM-DD, Got: {$fecha}";
}
}
return $errors;
}
/**
* Validate invoice numbers
*/
private function validateInvoiceNumbers(Invoice $invoice): array
{
$errors = [];
if ($invoice->getIdFactura() && method_exists($invoice->getIdFactura(), 'getNumSerieFactura')) {
$numero = $invoice->getIdFactura()->getNumSerieFactura();
// Check for common problematic patterns
if (str_contains($numero, 'TEST') && strlen($numero) < 10) {
$errors[] = "Test invoice numbers should be at least 10 characters long";
}
// Check for special characters that might cause issues
if (preg_match('/[^A-Za-z0-9\-_]/', $numero)) {
$errors[] = "Invoice number contains invalid characters. Only letters, numbers, hyphens and underscores allowed";
}
}
return $errors;
}
/**
* Validate amounts
@ -145,18 +115,18 @@ class InvoiceValidator
$errors = [];
// Check if desglose exists and has valid tax rates
if ($invoice->getDesglose()) {
$desglose = $invoice->getDesglose();
// if ($invoice->getDesglose()) {
// $desglose = $invoice->getDesglose();
// Validate tax rates are standard Spanish rates
$validRates = [0, 4, 10, 21];
// // Validate tax rates are standard Spanish rates
// $validRates = [0, 4, 10, 21];
// This would need to be implemented based on your Desglose structure
// $taxRate = $desglose->getTipoImpositivo();
// if (!in_array($taxRate, $validRates)) {
// $errors[] = "Invalid tax rate: {$taxRate}. Valid rates are: " . implode(', ', $validRates);
// // This would need to be implemented based on your Desglose structure
// // $taxRate = $desglose->getTipoImpositivo();
// // if (!in_array($taxRate, $validRates)) {
// // $errors[] = "Invalid tax rate: {$taxRate}. Valid rates are: " . implode(', ', $validRates);
// // }
// }
}
return $errors;
}

View File

@ -26,7 +26,7 @@ class XsltDocumentValidator
// private string $peppol_stylesheetx = 'Services/EDocument/Standards/Validation/Peppol/Stylesheets/ubl_stylesheet.xslt';
// private string $peppol_stylesheet = 'Services/EDocument/Standards/Validation/Peppol/Stylesheets/ci_to_ubl_stylesheet.xslt';
private array $errors = [];
public array $errors = [];
public function __construct(public string $xml_document)
{

View File

@ -60,7 +60,7 @@ class Verifactu extends AbstractService
if($v_logs->count() >= 1){
$v_log = $v_logs->first();
$huella = $v_log->hash;
$document = InvoiceModification::fromInvoice($this->invoice, $v_log->deserialize());
$document = InvoiceModification::createFromInvoice($document->getInvoice(), $v_log->deserialize());
}
//3. cancelled => RegistroAnulacion
@ -70,6 +70,8 @@ class Verifactu extends AbstractService
$soapXml = $document->toSoapEnvelope();
return $this;
}
/**

View File

@ -74,14 +74,7 @@ class AeatClient
nlog($parsedResponse);
if($parsedResponse['success']){
//write the success activity
}
else {
//handle the failure
}
return $parsedResponse;
}
}

View File

@ -7,8 +7,8 @@ use App\Services\EDocument\Standards\Verifactu\Models\RegistroAnterior;
class Encadenamiento extends BaseXmlModel
{
protected ?string $primerRegistro = null;
protected ?EncadenamientoFacturaAnterior $registroAnterior = null;
protected ?EncadenamientoFacturaAnterior $registroPosterior = null;
protected ?RegistroAnterior $registroAnterior = null;
protected ?RegistroAnterior $registroPosterior = null;
public function toXml(\DOMDocument $doc): \DOMElement
{
@ -55,7 +55,7 @@ class Encadenamiento extends BaseXmlModel
// Handle RegistroAnterior
$registroAnterior = $element->getElementsByTagNameNS(self::XML_NAMESPACE, 'RegistroAnterior')->item(0);
if ($registroAnterior) {
$encadenamiento->setRegistroAnterior(EncadenamientoFacturaAnterior::fromDOMElement($registroAnterior));
$encadenamiento->setRegistroAnterior(RegistroAnterior::fromDOMElement($registroAnterior));
}
return $encadenamiento;
@ -78,7 +78,7 @@ class Encadenamiento extends BaseXmlModel
// Handle RegistroAnterior
$registroAnterior = $element->getElementsByTagNameNS(self::XML_NAMESPACE, 'RegistroAnterior')->item(0);
if ($registroAnterior) {
$encadenamiento->setRegistroAnterior(EncadenamientoFacturaAnterior::fromDOMElement($registroAnterior));
$encadenamiento->setRegistroAnterior(RegistroAnterior::fromDOMElement($registroAnterior));
}
return $encadenamiento;
@ -109,109 +109,14 @@ class Encadenamiento extends BaseXmlModel
return $this;
}
public function getRegistroPosterior(): ?EncadenamientoFacturaAnterior
public function getRegistroPosterior(): ?RegistroAnterior
{
return $this->registroPosterior;
}
public function setRegistroPosterior(?EncadenamientoFacturaAnterior $registroPosterior): self
public function setRegistroPosterior(?RegistroAnterior $registroPosterior): self
{
$this->registroPosterior = $registroPosterior;
return $this;
}
}
class EncadenamientoFacturaAnterior extends BaseXmlModel
{
protected string $idEmisorFactura;
protected string $numSerieFactura;
protected string $fechaExpedicionFactura;
protected string $huella;
public function toXml(\DOMDocument $doc): \DOMElement
{
$root = $this->createElement($doc, 'RegistroAnterior');
$root->appendChild($this->createElement($doc, 'IDEmisorFactura', $this->idEmisorFactura));
$root->appendChild($this->createElement($doc, 'NumSerieFactura', $this->numSerieFactura));
$root->appendChild($this->createElement($doc, 'FechaExpedicionFactura', $this->fechaExpedicionFactura));
$root->appendChild($this->createElement($doc, 'Huella', $this->huella));
return $root;
}
public static function fromDOMElement(\DOMElement $element): self
{
$registroAnterior = new self();
// Handle IDEmisorFactura
$idEmisorFactura = $element->getElementsByTagNameNS(self::XML_NAMESPACE, 'IDEmisorFactura')->item(0);
if ($idEmisorFactura) {
$registroAnterior->setIdEmisorFactura($idEmisorFactura->nodeValue);
}
// Handle NumSerieFactura
$numSerieFactura = $element->getElementsByTagNameNS(self::XML_NAMESPACE, 'NumSerieFactura')->item(0);
if ($numSerieFactura) {
$registroAnterior->setNumSerieFactura($numSerieFactura->nodeValue);
}
// Handle FechaExpedicionFactura
$fechaExpedicionFactura = $element->getElementsByTagNameNS(self::XML_NAMESPACE, 'FechaExpedicionFactura')->item(0);
if ($fechaExpedicionFactura) {
$registroAnterior->setFechaExpedicionFactura($fechaExpedicionFactura->nodeValue);
}
// Handle Huella
$huella = $element->getElementsByTagNameNS(self::XML_NAMESPACE, 'Huella')->item(0);
if ($huella) {
$registroAnterior->setHuella($huella->nodeValue);
}
return $registroAnterior;
}
public function getIdEmisorFactura(): string
{
return $this->idEmisorFactura;
}
public function setIdEmisorFactura(string $idEmisorFactura): self
{
$this->idEmisorFactura = $idEmisorFactura;
return $this;
}
public function getNumSerieFactura(): string
{
return $this->numSerieFactura;
}
public function setNumSerieFactura(string $numSerieFactura): self
{
$this->numSerieFactura = $numSerieFactura;
return $this;
}
public function getFechaExpedicionFactura(): string
{
return $this->fechaExpedicionFactura;
}
public function setFechaExpedicionFactura(string $fechaExpedicionFactura): self
{
$this->fechaExpedicionFactura = $fechaExpedicionFactura;
return $this;
}
public function getHuella(): string
{
return $this->huella;
}
public function setHuella(string $huella): self
{
$this->huella = $huella;
return $this;
}
}

View File

@ -284,66 +284,66 @@ class InvoiceModification extends BaseXmlModel implements XmlModelInterface
/**
* Create a proper RegistroAlta structure from the RegistroModificacion data
*/
private function createRegistroAltaFromModificacion(\DOMDocument $doc): \DOMElement
{
$registroAlta = $doc->createElementNS(self::XML_NAMESPACE, self::XML_NAMESPACE_PREFIX . ':RegistroAlta');
// private function createRegistroAltaFromModificacion(\DOMDocument $doc): \DOMElement
// {
// $registroAlta = $doc->createElementNS(self::XML_NAMESPACE, self::XML_NAMESPACE_PREFIX . ':RegistroAlta');
// Add IDVersion
$registroAlta->appendChild($this->createElement($doc, 'IDVersion', $this->registroModificacion->getIdVersion()));
// // Add IDVersion
// $registroAlta->appendChild($this->createElement($doc, 'IDVersion', $this->registroModificacion->getIdVersion()));
// Create IDFactura structure
$idFactura = $doc->createElementNS(self::XML_NAMESPACE, self::XML_NAMESPACE_PREFIX . ':IDFactura');
$idFactura->appendChild($this->createElement($doc, 'IDEmisorFactura', $this->registroModificacion->getTercero()?->getNif() ?? 'B12345678'));
$idFactura->appendChild($this->createElement($doc, 'NumSerieFactura', $this->registroModificacion->getIdFactura()));
$idFactura->appendChild($this->createElement($doc, 'FechaExpedicionFactura', '2025-01-01'));
$registroAlta->appendChild($idFactura);
// // Create IDFactura structure
// $idFactura = $doc->createElementNS(self::XML_NAMESPACE, self::XML_NAMESPACE_PREFIX . ':IDFactura');
// $idFactura->appendChild($this->createElement($doc, 'IDEmisorFactura', $this->registroModificacion->getTercero()?->getNif() ?? 'B12345678'));
// $idFactura->appendChild($this->createElement($doc, 'NumSerieFactura', $this->registroModificacion->getIdFactura()));
// $idFactura->appendChild($this->createElement($doc, 'FechaExpedicionFactura', '2025-01-01'));
// $registroAlta->appendChild($idFactura);
// Add other required elements
if ($this->registroModificacion->getRefExterna()) {
$registroAlta->appendChild($this->createElement($doc, 'RefExterna', $this->registroModificacion->getRefExterna()));
}
$registroAlta->appendChild($this->createElement($doc, 'NombreRazonEmisor', $this->registroModificacion->getNombreRazonEmisor()));
$registroAlta->appendChild($this->createElement($doc, 'TipoFactura', $this->registroModificacion->getTipoFactura()));
$registroAlta->appendChild($this->createElement($doc, 'DescripcionOperacion', $this->registroModificacion->getDescripcionOperacion()));
// Add Desglose
$desglose = $doc->createElementNS(self::XML_NAMESPACE, self::XML_NAMESPACE_PREFIX . ':Desglose');
$desgloseFactura = $doc->createElementNS(self::XML_NAMESPACE, self::XML_NAMESPACE_PREFIX . ':DesgloseFactura');
$desgloseFactura->appendChild($this->createElement($doc, 'Impuesto', '01'));
$desgloseFactura->appendChild($this->createElement($doc, 'ClaveRegimen', '01'));
$desgloseFactura->appendChild($this->createElement($doc, 'CalificacionOperacion', 'S1'));
$desgloseFactura->appendChild($this->createElement($doc, 'TipoImpositivo', '21'));
$desgloseFactura->appendChild($this->createElement($doc, 'BaseImponibleOimporteNoSujeto', '100.00'));
$desgloseFactura->appendChild($this->createElement($doc, 'CuotaRepercutida', '21.00'));
$desglose->appendChild($desgloseFactura);
$registroAlta->appendChild($desglose);
$registroAlta->appendChild($this->createElement($doc, 'CuotaTotal', $this->registroModificacion->getCuotaTotal()));
$registroAlta->appendChild($this->createElement($doc, 'ImporteTotal', $this->registroModificacion->getImporteTotal()));
// Add Encadenamiento
$encadenamiento = $doc->createElementNS(self::XML_NAMESPACE, self::XML_NAMESPACE_PREFIX . ':Encadenamiento');
$encadenamiento->appendChild($this->createElement($doc, 'PrimerRegistro', 'S'));
$registroAlta->appendChild($encadenamiento);
// Add SistemaInformatico
$sistemaInformatico = $doc->createElementNS(self::XML_NAMESPACE, self::XML_NAMESPACE_PREFIX . ':SistemaInformatico');
$sistemaInformatico->appendChild($this->createElement($doc, 'NombreRazon', 'Test System'));
$sistemaInformatico->appendChild($this->createElement($doc, 'NIF', 'B12345678'));
$sistemaInformatico->appendChild($this->createElement($doc, 'NombreSistemaInformatico', 'Test Software'));
$sistemaInformatico->appendChild($this->createElement($doc, 'IdSistemaInformatico', '01'));
$sistemaInformatico->appendChild($this->createElement($doc, 'Version', '1.0'));
$sistemaInformatico->appendChild($this->createElement($doc, 'NumeroInstalacion', '001'));
$sistemaInformatico->appendChild($this->createElement($doc, 'TipoUsoPosibleSoloVerifactu', 'S'));
$sistemaInformatico->appendChild($this->createElement($doc, 'TipoUsoPosibleMultiOT', 'S'));
$sistemaInformatico->appendChild($this->createElement($doc, 'IndicadorMultiplesOT', 'S'));
$registroAlta->appendChild($sistemaInformatico);
$registroAlta->appendChild($this->createElement($doc, 'FechaHoraHusoGenRegistro', $this->registroModificacion->getFechaHoraHusoGenRegistro()));
$registroAlta->appendChild($this->createElement($doc, 'TipoHuella', $this->registroModificacion->getTipoHuella()));
$registroAlta->appendChild($this->createElement($doc, 'Huella', $this->registroModificacion->getHuella()));
return $registroAlta;
}
// // Add other required elements
// if ($this->registroModificacion->getRefExterna()) {
// $registroAlta->appendChild($this->createElement($doc, 'RefExterna', $this->registroModificacion->getRefExterna()));
// }
// $registroAlta->appendChild($this->createElement($doc, 'NombreRazonEmisor', $this->registroModificacion->getNombreRazonEmisor()));
// $registroAlta->appendChild($this->createElement($doc, 'TipoFactura', $this->registroModificacion->getTipoFactura()));
// $registroAlta->appendChild($this->createElement($doc, 'DescripcionOperacion', $this->registroModificacion->getDescripcionOperacion()));
// // Add Desglose
// $desglose = $doc->createElementNS(self::XML_NAMESPACE, self::XML_NAMESPACE_PREFIX . ':Desglose');
// $desgloseFactura = $doc->createElementNS(self::XML_NAMESPACE, self::XML_NAMESPACE_PREFIX . ':DesgloseFactura');
// $desgloseFactura->appendChild($this->createElement($doc, 'Impuesto', '01'));
// $desgloseFactura->appendChild($this->createElement($doc, 'ClaveRegimen', '01'));
// $desgloseFactura->appendChild($this->createElement($doc, 'CalificacionOperacion', 'S1'));
// $desgloseFactura->appendChild($this->createElement($doc, 'TipoImpositivo', '21'));
// $desgloseFactura->appendChild($this->createElement($doc, 'BaseImponibleOimporteNoSujeto', '100.00'));
// $desgloseFactura->appendChild($this->createElement($doc, 'CuotaRepercutida', '21.00'));
// $desglose->appendChild($desgloseFactura);
// $registroAlta->appendChild($desglose);
// $registroAlta->appendChild($this->createElement($doc, 'CuotaTotal', $this->registroModificacion->getCuotaTotal()));
// $registroAlta->appendChild($this->createElement($doc, 'ImporteTotal', $this->registroModificacion->getImporteTotal()));
// // Add Encadenamiento
// $encadenamiento = $doc->createElementNS(self::XML_NAMESPACE, self::XML_NAMESPACE_PREFIX . ':Encadenamiento');
// $encadenamiento->appendChild($this->createElement($doc, 'PrimerRegistro', 'S'));
// $registroAlta->appendChild($encadenamiento);
// // Add SistemaInformatico
// $sistemaInformatico = $doc->createElementNS(self::XML_NAMESPACE, self::XML_NAMESPACE_PREFIX . ':SistemaInformatico');
// $sistemaInformatico->appendChild($this->createElement($doc, 'NombreRazon', 'Test System'));
// $sistemaInformatico->appendChild($this->createElement($doc, 'NIF', 'B12345678'));
// $sistemaInformatico->appendChild($this->createElement($doc, 'NombreSistemaInformatico', 'Test Software'));
// $sistemaInformatico->appendChild($this->createElement($doc, 'IdSistemaInformatico', '01'));
// $sistemaInformatico->appendChild($this->createElement($doc, 'Version', '1.0'));
// $sistemaInformatico->appendChild($this->createElement($doc, 'NumeroInstalacion', '001'));
// $sistemaInformatico->appendChild($this->createElement($doc, 'TipoUsoPosibleSoloVerifactu', 'S'));
// $sistemaInformatico->appendChild($this->createElement($doc, 'TipoUsoPosibleMultiOT', 'S'));
// $sistemaInformatico->appendChild($this->createElement($doc, 'IndicadorMultiplesOT', 'S'));
// $registroAlta->appendChild($sistemaInformatico);
// $registroAlta->appendChild($this->createElement($doc, 'FechaHoraHusoGenRegistro', $this->registroModificacion->getFechaHoraHusoGenRegistro()));
// $registroAlta->appendChild($this->createElement($doc, 'TipoHuella', $this->registroModificacion->getTipoHuella()));
// $registroAlta->appendChild($this->createElement($doc, 'Huella', $this->registroModificacion->getHuella()));
// return $registroAlta;
// }
}

View File

@ -103,7 +103,6 @@ class RegistroAlta
{
// Get the previous invoice log
/** @var ?VerifactuLog $v_log */
$this->v_log = $this->company->verifactu_logs()->first();
$this->current_timestamp = now()->setTimezone('Europe/Madrid')->format('Y-m-d\TH:i:s');
@ -171,10 +170,10 @@ class RegistroAlta
if($this->v_log){
$registro_anterior = new RegistroAnterior();
$registro_anterior->setIDEmisorFactura($v_log->nif);
$registro_anterior->setNumSerieFactura($v_log->invoice_number);
$registro_anterior->setFechaExpedicionFactura($v_log->date->format('d-m-Y'));
$registro_anterior->setHuella($v_log->hash);
$registro_anterior->setIDEmisorFactura($this->v_log->nif);
$registro_anterior->setNumSerieFactura($this->v_log->invoice_number);
$registro_anterior->setFechaExpedicionFactura($this->v_log->date->format('d-m-Y'));
$registro_anterior->setHuella($this->v_log->hash);
$encadenamiento->setRegistroAnterior($registro_anterior);
@ -206,6 +205,11 @@ class RegistroAlta
return $this;
}
public function getInvoice(): VerifactuInvoice
{
return $this->v_invoice;
}
private function calculateTaxType(string $tax_name): string
{
if(stripos($tax_name, 'iva') !== false) {

View File

@ -265,7 +265,8 @@ class ResponseProcessor
$nodeList = $xpathObj->query($xpath);
if ($nodeList && $nodeList->length > 0) {
return $nodeList->item(0);
$node = $nodeList->item(0);
return $node instanceof DOMElement ? $node : null;
}
return null;

View File

@ -97,38 +97,38 @@ class QuickbooksService
return $this;
}
private function checkDefaultAccounts(): self
{
// private function checkDefaultAccounts(): self
// {
$accountQuery = "SELECT * FROM Account WHERE AccountType IN ('Income', 'Cost of Goods Sold')";
// $accountQuery = "SELECT * FROM Account WHERE AccountType IN ('Income', 'Cost of Goods Sold')";
if (strlen($this->settings->default_income_account) == 0 || strlen($this->settings->default_expense_account) == 0) {
// if (strlen($this->settings->default_income_account) == 0 || strlen($this->settings->default_expense_account) == 0) {
nlog("Checking default accounts for company {$this->company->company_key}");
$accounts = $this->sdk->Query($accountQuery);
// nlog("Checking default accounts for company {$this->company->company_key}");
// $accounts = $this->sdk->Query($accountQuery);
$find_income_account = true;
$find_expense_account = true;
// $find_income_account = true;
// $find_expense_account = true;
foreach ($accounts as $account) {
if ($account->AccountType->value == 'Income' && $find_income_account) {
$this->settings->default_income_account = $account->Id->value;
$find_income_account = false;
} elseif ($account->AccountType->value == 'Cost of Goods Sold' && $find_expense_account) {
$this->settings->default_expense_account = $account->Id->value;
$find_expense_account = false;
}
}
// foreach ($accounts as $account) {
// if ($account->AccountType->value == 'Income' && $find_income_account) {
// $this->settings->default_income_account = $account->Id->value;
// $find_income_account = false;
// } elseif ($account->AccountType->value == 'Cost of Goods Sold' && $find_expense_account) {
// $this->settings->default_expense_account = $account->Id->value;
// $find_expense_account = false;
// }
// }
nlog($this->settings);
// nlog($this->settings);
$this->company->quickbooks->settings = $this->settings;
$this->company->save();
}
// $this->company->quickbooks->settings = $this->settings;
// $this->company->save();
// }
return $this;
}
// return $this;
// }
private function checkToken(): self
{

View File

@ -130,25 +130,25 @@ class QuoteTransformer extends BaseTransformer
}
private function getPayments(mixed $qb_data)
{
$payments = [];
// private function getPayments(mixed $qb_data)
// {
// $payments = [];
$qb_payments = data_get($qb_data, 'LinkedTxn', false) ?? [];
// $qb_payments = data_get($qb_data, 'LinkedTxn', false) ?? [];
if (!empty($qb_payments) && !isset($qb_payments[0])) {
$qb_payments = [$qb_payments];
}
// if (!empty($qb_payments) && !isset($qb_payments[0])) {
// $qb_payments = [$qb_payments];
// }
foreach ($qb_payments as $payment) {
if (data_get($payment, 'TxnType', false) == 'Payment') {
$payments[] = data_get($payment, 'TxnId', false);
}
}
// foreach ($qb_payments as $payment) {
// if (data_get($payment, 'TxnType', false) == 'Payment') {
// $payments[] = data_get($payment, 'TxnId', false);
// }
// }
return $payments;
// return $payments;
}
// }
private function getLineItems(mixed $qb_data, array $tax_array)
{

View File

@ -757,7 +757,7 @@ class HtmlEngine
if ($this->entity_string == 'invoice' && $this->entity->net_payments()->exists()) {
$payment_list = '<br><br>';
foreach ($this->entity->net_payments as $payment) {
foreach ($this->entity->net_payments as $payment) { //@phpstan-ignore-line
$payment_list .= ctrans('texts.payment_subject') . ": " . $this->formatDate($payment->date, $this->client->date_format()) . " :: " . Number::formatMoney($payment->amount, $this->client) ." :: ". $payment->translatedType() . "<br>";
}

10
composer.lock generated
View File

@ -5384,16 +5384,16 @@
},
{
"name": "laravel/octane",
"version": "v2.12.0",
"version": "v2.12.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/octane.git",
"reference": "d606f3dffc785032f11c23a017334c99800f2e40"
"reference": "4ca38b90d76f31b8c1e27873316c2db34450151c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/octane/zipball/d606f3dffc785032f11c23a017334c99800f2e40",
"reference": "d606f3dffc785032f11c23a017334c99800f2e40",
"url": "https://api.github.com/repos/laravel/octane/zipball/4ca38b90d76f31b8c1e27873316c2db34450151c",
"reference": "4ca38b90d76f31b8c1e27873316c2db34450151c",
"shasum": ""
},
"require": {
@ -5470,7 +5470,7 @@
"issues": "https://github.com/laravel/octane/issues",
"source": "https://github.com/laravel/octane"
},
"time": "2025-07-18T15:50:14+00:00"
"time": "2025-07-25T15:03:05+00:00"
},
{
"name": "laravel/prompts",

View File

@ -20,6 +20,7 @@ parameters:
- 'app/PaymentDrivers/AuthorizePaymentDriver.php'
- 'app/Http/Middleware/ThrottleRequestsWithPredis.php'
- 'app/Utils/Traits/*'
- 'Modules/Accounting/*'
universalObjectCratesClasses:
- App\DataMapper\Tax\RuleInterface
- App\DataMapper\FeesAndLimits

View File

@ -155,7 +155,6 @@ class InvoiceCancellationTest extends TestCase
$this->assertEquals('02', $cancellation->getEstado());
$this->assertEquals('Factura anulada por error', $cancellation->getDescripcionEstado());
nlog($cancellation->toXmlString());
}
public function testInvoiceCancellationXmlGeneration()

View File

@ -127,7 +127,7 @@ class VerifactuFeatureTest extends TestCase
$item = new InvoiceItem();
$item->product_key = '1234567890';
$item->qty = 1;
$item->quantity = 1;
$item->cost = 100;
$item->notes = 'Test item';
$item->tax_name1 = 'IVA';