Integration works for Verifactu
This commit is contained in:
parent
ff92756dbc
commit
3d3b5f6938
|
|
@ -51,6 +51,7 @@ class InvoiceBackupCast implements CastsAttributes
|
||||||
'document_type' => $value->document_type,
|
'document_type' => $value->document_type,
|
||||||
'child_invoice_ids' => $value->child_invoice_ids->toArray(),
|
'child_invoice_ids' => $value->child_invoice_ids->toArray(),
|
||||||
'redirect' => $value->redirect,
|
'redirect' => $value->redirect,
|
||||||
|
'adjustable_amount' => $value->adjustable_amount,
|
||||||
])
|
])
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,10 @@ class InvoiceBackup implements Castable
|
||||||
public Cancellation $cancellation = new Cancellation(0,0),
|
public Cancellation $cancellation = new Cancellation(0,0),
|
||||||
public ?string $parent_invoice_id = null, // The id of the invoice that was cancelled
|
public ?string $parent_invoice_id = null, // The id of the invoice that was cancelled
|
||||||
public ?string $parent_invoice_number = null, // The number of the invoice that was cancelled
|
public ?string $parent_invoice_number = null, // The number of the invoice that was cancelled
|
||||||
public ?string $document_type = null, // The reason for the cancellation
|
public ?string $document_type = null, // F1, R2
|
||||||
public Collection $child_invoice_ids = new Collection(), // Collection of child invoice IDs
|
public Collection $child_invoice_ids = new Collection(), // Collection of child invoice IDs
|
||||||
public ?string $redirect = null, // The redirect url for the invoice
|
public ?string $redirect = null, // The redirect url for the invoice
|
||||||
|
public float $adjustable_amount = 0,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -51,7 +52,8 @@ class InvoiceBackup implements Castable
|
||||||
parent_invoice_number: $data['parent_invoice_number'] ?? null,
|
parent_invoice_number: $data['parent_invoice_number'] ?? null,
|
||||||
document_type: $data['document_type'] ?? null,
|
document_type: $data['document_type'] ?? null,
|
||||||
child_invoice_ids: isset($data['child_invoice_ids']) ? collect($data['child_invoice_ids']) : new Collection(),
|
child_invoice_ids: isset($data['child_invoice_ids']) ? collect($data['child_invoice_ids']) : new Collection(),
|
||||||
redirect: $data['redirect'] ?? null
|
redirect: $data['redirect'] ?? null,
|
||||||
|
adjustable_amount: $data['adjustable_amount'] ?? 0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,8 +91,7 @@ class StoreInvoiceRequest extends Request
|
||||||
$rules['custom_surcharge4'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
$rules['custom_surcharge4'] = ['sometimes', 'nullable', 'bail', 'numeric', 'max:99999999999999'];
|
||||||
$rules['location_id'] = ['nullable', 'sometimes','bail', Rule::exists('locations', 'id')->where('company_id', $user->company()->id)->where('client_id', $this->client_id)];
|
$rules['location_id'] = ['nullable', 'sometimes','bail', Rule::exists('locations', 'id')->where('company_id', $user->company()->id)->where('client_id', $this->client_id)];
|
||||||
|
|
||||||
$rules['verifactu_modified'] = ['bail', 'boolean', 'required_with:modified_invoice_id'];
|
$rules['modified_invoice_id'] = ['bail', 'sometimes', 'nullable', new CanGenerateModificationInvoice()];
|
||||||
$rules['modified_invoice_id'] = ['bail', 'required_with:verifactu_modified', new CanGenerateModificationInvoice()];
|
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,6 @@ class CanGenerateModificationInvoice implements ValidationRule
|
||||||
$fail("No se puede crear una factura de rectificación cuando se ha realizado un pago."); // Cannot create a rectification invoice where a payment has been made
|
$fail("No se puede crear una factura de rectificación cuando se ha realizado un pago."); // Cannot create a rectification invoice where a payment has been made
|
||||||
} elseif($invoice->status_id === Invoice::STATUS_CANCELLED ) {
|
} elseif($invoice->status_id === Invoice::STATUS_CANCELLED ) {
|
||||||
$fail("No se puede crear una factura de rectificación para una factura cancelada."); // Cannot create a rectification invoice for a cancelled invoice
|
$fail("No se puede crear una factura de rectificación para una factura cancelada."); // Cannot create a rectification invoice for a cancelled invoice
|
||||||
} elseif($invoice->status_id === Invoice::STATUS_REPLACED) {
|
|
||||||
$fail("No se puede crear una factura de rectificación para una factura reemplazada."); // Cannot create a rectification invoice for a replaced invoice
|
|
||||||
} elseif($invoice->status_id === Invoice::STATUS_REVERSED) {
|
} elseif($invoice->status_id === Invoice::STATUS_REVERSED) {
|
||||||
$fail("No se puede crear una factura de rectificación para una factura revertida."); // Cannot create a rectification invoice for a reversed invoice
|
$fail("No se puede crear una factura de rectificación para una factura revertida."); // Cannot create a rectification invoice for a reversed invoice
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
namespace App\Http\ValidationRules\Invoice;
|
namespace App\Http\ValidationRules\Invoice;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
|
use App\Models\Client;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Contracts\Validation\ValidationRule;
|
use Illuminate\Contracts\Validation\ValidationRule;
|
||||||
|
|
@ -39,6 +40,12 @@ class VerifactuAmountCheck implements ValidationRule
|
||||||
$company = $user->company();
|
$company = $user->company();
|
||||||
|
|
||||||
if ($company->verifactuEnabled()) {
|
if ($company->verifactuEnabled()) {
|
||||||
|
|
||||||
|
$client = Client::withTrashed()->find($this->input['client_id']);
|
||||||
|
|
||||||
|
if($client->country->iso_3166_2 !== 'ES') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$invoice = false;
|
$invoice = false;
|
||||||
$child_invoices = false;
|
$child_invoices = false;
|
||||||
|
|
@ -47,12 +54,18 @@ class VerifactuAmountCheck implements ValidationRule
|
||||||
|
|
||||||
if(isset($this->input['modified_invoice_id'])) {
|
if(isset($this->input['modified_invoice_id'])) {
|
||||||
$invoice = Invoice::withTrashed()->where('id', $this->decodePrimaryKey($this->input['modified_invoice_id']))->company()->firstOrFail();
|
$invoice = Invoice::withTrashed()->where('id', $this->decodePrimaryKey($this->input['modified_invoice_id']))->company()->firstOrFail();
|
||||||
|
|
||||||
|
if ($invoice->backup->adjustable_amount <= 0) {
|
||||||
|
$fail("Invoice already credited in full");
|
||||||
|
}
|
||||||
|
|
||||||
$child_invoices = Invoice::withTrashed()
|
$child_invoices = Invoice::withTrashed()
|
||||||
->whereIn('id', $this->transformKeys($invoice->backup->child_invoice_ids->toArray()))
|
->whereIn('id', $this->transformKeys($invoice->backup->child_invoice_ids->toArray()))
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$child_invoice_totals = round($child_invoices->sum('amount'), 2);
|
$child_invoice_totals = round($child_invoices->sum('amount'), 2);
|
||||||
$child_invoice_count = $child_invoices->count();
|
$child_invoice_count = $child_invoices->count();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$items = collect($this->input['line_items'])->map(function ($item) use($company){
|
$items = collect($this->input['line_items'])->map(function ($item) use($company){
|
||||||
|
|
@ -84,10 +97,7 @@ class VerifactuAmountCheck implements ValidationRule
|
||||||
|
|
||||||
$total = $items->sum() - $total_discount;
|
$total = $items->sum() - $total_discount;
|
||||||
|
|
||||||
if($total > 0) {
|
if($total < 0 && !$invoice) {
|
||||||
$fail("Only negative amounts allowed for rectification {$total}");
|
|
||||||
}
|
|
||||||
elseif($total < 0 && !$invoice) {
|
|
||||||
$fail("Negative invoices {$total} can only be linked to existing invoices");
|
$fail("Negative invoices {$total} can only be linked to existing invoices");
|
||||||
}
|
}
|
||||||
elseif($invoice && ($total + $child_invoice_totals + $invoice->amount) < 0) {
|
elseif($invoice && ($total + $child_invoice_totals + $invoice->amount) < 0) {
|
||||||
|
|
|
||||||
|
|
@ -248,8 +248,6 @@ class Invoice extends BaseModel
|
||||||
|
|
||||||
public const STATUS_UNPAID = -2; // status < 4 || < 3 && !is_deleted && !trashed()
|
public const STATUS_UNPAID = -2; // status < 4 || < 3 && !is_deleted && !trashed()
|
||||||
|
|
||||||
public const STATUS_REPLACED = 7; // handle the case where the invoice is replaced by another invoice.
|
|
||||||
|
|
||||||
public function toSearchableArray()
|
public function toSearchableArray()
|
||||||
{
|
{
|
||||||
$locale = $this->company->locale();
|
$locale = $this->company->locale();
|
||||||
|
|
|
||||||
|
|
@ -330,8 +330,8 @@ class BaseRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Verifactu modified invoice check */
|
/** Verifactu modified invoice check */
|
||||||
if(isset($data['verifactu_modified']) && $data['verifactu_modified']) {
|
if($model->company->verifactuEnabled() && $client->country->iso_3166_2 === 'ES') {
|
||||||
$model->service()->modifyVerifactuWorkflow($data['modified_invoice_id'])->save();
|
$model->service()->modifyVerifactuWorkflow($data, $this->new_model)->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -710,35 +710,35 @@ class InvoiceService
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* modifyVerifactuWorkflow
|
* Handles all requirements for verifactu saves
|
||||||
* @todo - handle invoice modifications - ensure when we
|
|
||||||
* sent this to AEAT we reference the invoice that was replaced.
|
|
||||||
*
|
*
|
||||||
* @param string $modified_invoice_hashed_id
|
* @param array $invoice_array
|
||||||
|
* @param bool $new_model
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
public function modifyVerifactuWorkflow(string $modified_invoice_hashed_id): self
|
public function modifyVerifactuWorkflow(array $invoice_array, bool $new_model): self
|
||||||
{
|
{
|
||||||
//if the new invoice has a negative amount - then it is not a replacement, it is a
|
if($new_model && $this->invoice->amount >= 0) {
|
||||||
//delta modification on an existing invoice.
|
$this->invoice->backup->document_type = 'F1';
|
||||||
$modified_invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($modified_invoice_hashed_id));
|
$this->invoice->backup->adjustable_amount = $this->invoice->amount;
|
||||||
|
$this->invoice->saveQuietly();
|
||||||
if($this->invoice->amount > 0) {
|
}
|
||||||
$modified_invoice->status_id = Invoice::STATUS_REPLACED;
|
elseif(isset($invoice_array['modified_invoice_id'])) {
|
||||||
|
$modified_invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($invoice_array['modified_invoice_id']));
|
||||||
|
$modified_invoice->backup->child_invoice_ids->push($this->invoice->hashed_id);
|
||||||
|
$modified_invoice->backup->adjustable_amount += $this->invoice->amount;
|
||||||
|
$modified_invoice->save();
|
||||||
|
|
||||||
|
$this->markSent();
|
||||||
|
//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->document_type = 'R2';
|
||||||
|
$this->invoice->saveQuietly();
|
||||||
|
|
||||||
|
$this->invoice->client->service()->updateBalance(round(($this->invoice->amount - $modified_invoice->amount), 2));
|
||||||
|
$this->sendVerifactu();
|
||||||
}
|
}
|
||||||
|
|
||||||
$modified_invoice->backup->child_invoice_ids->push($this->invoice->hashed_id);
|
|
||||||
$modified_invoice->save();
|
|
||||||
|
|
||||||
$this->markSent();
|
|
||||||
//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->document_type = 'F3';
|
|
||||||
$this->invoice->saveQuietly();
|
|
||||||
|
|
||||||
$this->invoice->client->service()->updateBalance(round(($this->invoice->amount - $modified_invoice->amount), 2));
|
|
||||||
$this->sendVerifactu();
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5575,6 +5575,7 @@ $lang = array(
|
||||||
'create_company_error_unauthorized' => 'You are not authorized to create a company. Only the account owner can create a company.',
|
'create_company_error_unauthorized' => 'You are not authorized to create a company. Only the account owner can create a company.',
|
||||||
'restore_disabled_verifactu' => 'You cannot restore an invoice once it has been deleted',
|
'restore_disabled_verifactu' => 'You cannot restore an invoice once it has been deleted',
|
||||||
'delete_disabled_verifactu' => 'You cannot delete an invoice once it has been cancelled or modified',
|
'delete_disabled_verifactu' => 'You cannot delete an invoice once it has been cancelled or modified',
|
||||||
|
'rectify' => 'Rectificar',
|
||||||
);
|
);
|
||||||
|
|
||||||
return $lang;
|
return $lang;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue