invoiceninja/app/Http/ValidationRules/Invoice/VerifactuAmountCheck.php

142 lines
6.0 KiB
PHP

<?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\Http\ValidationRules\Invoice;
use Closure;
use App\Utils\BcMath;
use App\Models\Client;
use App\Models\Invoice;
use App\Utils\Traits\MakesHash;
use Illuminate\Contracts\Validation\ValidationRule;
/**
* Class VerifactuAmountCheck.
*/
class VerifactuAmountCheck implements ValidationRule
{
use MakesHash;
public function __construct(private array $input){}
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (empty($value)) {
return;
}
$user = auth()->user();
$company = $user->company();
if ($company->verifactuEnabled() && isset($this->input['modified_invoice_id'])) { // Company level check if Verifactu is enabled
/** Harvest the parent invoice to check balances available for cancellation */
$invoice = Invoice::withTrashed()->where('id', $this->decodePrimaryKey($this->input['modified_invoice_id']))->company()->first();
if(!$invoice) {
$fail("Factura no encontrada."); // Invoice not found
} elseif($invoice->is_deleted) {
$fail("No se puede crear una factura de rectificación para una factura eliminada."); // Cannot create a rectification invoice for a deleted invoice
} elseif($invoice->backup->document_type !== 'F1') {
$fail("Solo las facturas originales F1 pueden ser rectificadas."); // Only original F1 invoices can be rectified
} elseif($invoice->status_id === Invoice::STATUS_DRAFT){
$fail("No se puede crear una factura de rectificación para una factura en borrador."); // Cannot create a rectification invoice for a draft invoice
} elseif(in_array($invoice->status_id, [Invoice::STATUS_PARTIAL, Invoice::STATUS_PAID])) {
$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 ) {
$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_REVERSED) {
$fail("No se puede crear una factura de rectificación para una factura revertida."); // Cannot create a rectification invoice for a reversed invoice
}
/** Sum previously refunded amounts */
$child_invoices_sum = Invoice::withTrashed()
->whereIn('id', $this->transformKeys($invoice->backup->child_invoice_ids->toArray()))
->get()
->sum('backup.adjustable_amount');
$child_invoices_sum = abs($child_invoices_sum);
/** Balance left to be cancelled */
$adjustable_amount = $invoice->backup->adjustable_amount - $child_invoices_sum;
if (BcMath::comp($adjustable_amount, 0) == 0) {
$fail("Invoice already credited in full");
}
// \DB::connection(config('database.default'))->beginTransaction();
$array_data = request()->all();
unset($array_data['client_id']);
$invoice->fill($array_data);
/** Calculate the gross total of the cancellation invoice. */
// $total = $invoice->calc()->getTotal();
// $items = $array_data['line_items'];
// /** Remove IRPF from the cancellation invoice. */
// foreach($items as &$item){
// if(stripos($item['tax_name1'], 'irpf') !== false){
// $item['tax_name1'] = '';
// $item['tax_rate1'] = 0;
// }
// elseif(stripos($item['tax_name2'], 'irpf') !== false){
// $item['tax_name2'] = '';
// $item['tax_rate2'] = 0;
// }
// elseif(stripos($item['tax_name3'], 'irpf') !== false){
// $item['tax_name3'] = '';
// $item['tax_rate3'] = 0;
// }
// }
// $invoice->line_items = $items;
/** Total WITHOUT IRPF */
$total = $invoice->calc()->getTotal();
$invoice->refresh();
// \DB::connection(config('database.default'))->rollBack();
if($total > 0) {
$fail("Only negative invoices can rectify a invoice.");
}
/** The Calculated amount that can be cancelled */
$adjustable_amount = $invoice->backup->adjustable_amount - $child_invoices_sum;
/** The client facing amount that can be cancelled This is the amount that will NOT contain IRPF amounts */
$client_facing_adjustable_amount = ($invoice->amount / $invoice->backup->adjustable_amount) * $adjustable_amount;
nlog("total: " . $total);
nlog("invoice->amount: " . $invoice->amount);
nlog("adjustable_amount: " . $adjustable_amount);
nlog("child_invoices_sum: " . $child_invoices_sum);
nlog("invoice->backup->adjustable_amount: " . $invoice->backup->adjustable_amount);
nlog("client_facing_adjustable_amount: " . $client_facing_adjustable_amount);
if(abs($total) > $client_facing_adjustable_amount) {
$fail("Total de ajuste {$total} no puede exceder el saldo de la factura {$client_facing_adjustable_amount}");
}
}
}
}