1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-13 06:32:40 +01:00

Merge pull request #8462 from turbo124/v5-develop

V5 develop
This commit is contained in:
David Bomba 2023-04-23 08:16:45 +10:00 committed by GitHub
commit b9bcbe0015
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 3241 additions and 2916 deletions

View File

@ -11,11 +11,12 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Libraries\MultiDB; use App\Utils\Ninja;
use App\Models\Backup; use App\Models\Backup;
use App\Models\Client; use App\Models\Client;
use App\Models\Company; use App\Models\Company;
use App\Models\Document; use App\Models\Document;
use App\Libraries\MultiDB;
use App\Models\GroupSetting; use App\Models\GroupSetting;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
@ -55,6 +56,9 @@ class BackupUpdate extends Command
{ {
//always return state to first DB //always return state to first DB
if(Ninja::isSelfHost())
return;
$current_db = config('database.default'); $current_db = config('database.default');
if (! config('ninja.db.multi_db_enabled')) { if (! config('ninja.db.multi_db_enabled')) {

View File

@ -12,6 +12,7 @@
namespace App\DataMapper\Tax; namespace App\DataMapper\Tax;
use App\Models\Client; use App\Models\Client;
use App\Models\Invoice;
use App\Models\Product; use App\Models\Product;
use App\DataMapper\Tax\ZipTax\Response; use App\DataMapper\Tax\ZipTax\Response;
@ -117,6 +118,8 @@ class BaseRule implements RuleInterface
protected ?Response $tax_data; protected ?Response $tax_data;
public ?Invoice $invoice;
public function __construct() public function __construct()
{ {
} }
@ -126,18 +129,29 @@ class BaseRule implements RuleInterface
return $this; return $this;
} }
public function setClient(Client $client): self public function setInvoice(Invoice $invoice): self
{ {
$this->client = $client; $this->invoice = $invoice;
$this->configTaxData();
$this->client = $invoice->client;
$this->tax_data = new Response($this->invoice->tax_data);
$this->resolveRegions(); $this->resolveRegions();
return $this; return $this;
} }
public function setTaxData(Response $tax_data): self private function configTaxData(): self
{ {
$this->tax_data = $tax_data; if($this->invoice->tax_data && $this->invoice->status_id > 1)
return $this;
//determine if we are taxing locally or if we are taxing globally
// $this->invoice->tax_data = $this->invoice->client->tax_data;
return $this; return $this;
} }
@ -176,10 +190,10 @@ class BaseRule implements RuleInterface
return $this; return $this;
} }
elseif($this->client_region == 'AU'){ elseif($this->client_region == 'AU'){ //these are defaults and are only stubbed out for now, for AU we can actually remove these
$this->tax_rate1 = 10; $this->tax_rate1 = $this->client->company->tax_data->regions->AU->subregions->AU->tax_rate;
$this->tax_name1 = 'GST'; $this->tax_name1 = $this->client->company->tax_data->regions->AU->subregions->AU->tax_name;
return $this; return $this;
} }

View File

@ -14,19 +14,19 @@ namespace App\DataMapper\Tax;
class TaxModel class TaxModel
{ {
/** @var mixed $seller_subregion */ /** @var string $seller_subregion */
public string $seller_subregion = 'CA'; public string $seller_subregion = 'CA';
/** @var mixed $version */ /** @var string $version */
public string $version = 'alpha'; public string $version = 'alpha';
/** @var mixed $regions */ /** @var object $regions */
public object $regions; public object $regions;
/** /**
* __construct * __construct
* *
* @param mixed $model * @param TaxModel $model
* @return void * @return void
*/ */
public function __construct(public ?TaxModel $model = null) public function __construct(public ?TaxModel $model = null)
@ -42,9 +42,9 @@ class TaxModel
/** /**
* Initializes the rules and builds any required data. * Initializes the rules and builds any required data.
* *
* @return void * @return object
*/ */
public function init() public function init(): object
{ {
$this->regions = new \stdClass(); $this->regions = new \stdClass();
$this->regions->US = new \stdClass(); $this->regions->US = new \stdClass();

View File

@ -65,6 +65,7 @@ class Response
public string $geoCounty = ""; public string $geoCounty = "";
public string $geoState = ""; public string $geoState = "";
public float $taxSales = 0; public float $taxSales = 0;
public string $taxName = "";
public float $taxUse = 0; public float $taxUse = 0;
public string $txbService = ""; // N = No, Y = Yes public string $txbService = ""; // N = No, Y = Yes
public string $txbFreight = ""; // N = No, Y = Yes public string $txbFreight = ""; // N = No, Y = Yes
@ -73,6 +74,8 @@ class Response
public float $citySalesTax = 0; public float $citySalesTax = 0;
public float $cityUseTax = 0; public float $cityUseTax = 0;
public string $cityTaxCode = ""; public string $cityTaxCode = "";
/* US SPECIFIC TAX CODES */
public float $countySalesTax = 0; public float $countySalesTax = 0;
public float $countyUseTax = 0; public float $countyUseTax = 0;
public string $countyTaxCode = ""; public string $countyTaxCode = "";
@ -93,7 +96,9 @@ class Response
public string $district5Code = ""; public string $district5Code = "";
public float $district5SalesTax = 0; public float $district5SalesTax = 0;
public float $district5UseTax = 0; public float $district5UseTax = 0;
public string $originDestination = ""; /* US SPECIFIC TAX CODES */
public string $originDestination = ""; // defines if the client origin is the locale where the tax is remitted to
public function __construct($data) public function __construct($data)
{ {

View File

@ -116,7 +116,7 @@ class RecurringInvoiceFilters extends QueryFilters
/** /**
* Filters the query by the users company ID. * Filters the query by the users company ID.
* *
* @return Illuminate\Database\Eloquent\Builder * @return Builder
*/ */
public function entityFilter(): Builder public function entityFilter(): Builder
{ {
@ -126,7 +126,7 @@ class RecurringInvoiceFilters extends QueryFilters
/** /**
* Filter based on line_items product_key * Filter based on line_items product_key
* *
* @param string value Product keys * @param string $value Product keys
* @return Builder * @return Builder
*/ */
public function product_key(string $value = ''): Builder public function product_key(string $value = ''): Builder

View File

@ -146,12 +146,9 @@ class InvoiceItemSum
$class = "App\DataMapper\Tax\\".$this->client->company->country()->iso_3166_2."\\Rule"; $class = "App\DataMapper\Tax\\".$this->client->company->country()->iso_3166_2."\\Rule";
$tax_data = new Response($this->invoice?->tax_data);
$this->rule = new $class(); $this->rule = new $class();
$this->rule $this->rule
->setTaxData($tax_data) ->setInvoice($this->invoice)
->setClient($this->client)
->init(); ->init();
$this->calc_tax = true; $this->calc_tax = true;

View File

@ -140,10 +140,6 @@ class EmailController extends BaseController
$mo->cc[] = new Address($request->cc_email); $mo->cc[] = new Address($request->cc_email);
} }
// if ($entity == 'purchaseOrder' || $entity == 'purchase_order' || $template == 'purchase_order' || $entity == 'App\Models\PurchaseOrder') {
// return $this->sendPurchaseOrder($entity_obj, $data, $template);
// }
$entity_obj->invitations->each(function ($invitation) use ($data, $entity_obj, $template, $mo) { $entity_obj->invitations->each(function ($invitation) use ($data, $entity_obj, $template, $mo) {
if (! $invitation->contact->trashed() && $invitation->contact->email) { if (! $invitation->contact->trashed() && $invitation->contact->email) {
$entity_obj->service()->markSent()->save(); $entity_obj->service()->markSent()->save();

View File

@ -162,6 +162,9 @@ class SelfUpdateController extends BaseController
$this->deleteDirectory(base_path('vendor/beganovich/snappdf/versions/'.$file->getFileName())); $this->deleteDirectory(base_path('vendor/beganovich/snappdf/versions/'.$file->getFileName()));
} }
} }
$iterator = null;
} }
private function deleteDirectory($dir) private function deleteDirectory($dir)
@ -206,6 +209,8 @@ class SelfUpdateController extends BaseController
foreach (new \RecursiveIteratorIterator($directoryIterator) as $file) { foreach (new \RecursiveIteratorIterator($directoryIterator) as $file) {
unlink(base_path('bootstrap/cache/').$file->getFileName()); unlink(base_path('bootstrap/cache/').$file->getFileName());
} }
$directoryIterator = null;
} }
private function testWritable() private function testWritable()
@ -225,6 +230,8 @@ class SelfUpdateController extends BaseController
} }
} }
$directoryIterator = null;
return true; return true;
} }

View File

@ -30,6 +30,7 @@ class BlackListRule implements Rule
'sharklasers.com', 'sharklasers.com',
'100072641.help', '100072641.help',
'yandex.com', 'yandex.com',
'bloheyz.com',
]; ];
/** /**

View File

@ -136,7 +136,7 @@ class NinjaMailerJob implements ShouldQueue
->send($this->nmo->mailable); ->send($this->nmo->mailable);
/* Count the amount of emails sent across all the users accounts */ /* Count the amount of emails sent across all the users accounts */
Cache::increment($this->company->account->key); Cache::increment("email_quota".$this->company->account->key);
LightLogs::create(new EmailSuccess($this->nmo->company->company_key)) LightLogs::create(new EmailSuccess($this->nmo->company->company_key))
->send(); ->send();
@ -486,8 +486,13 @@ class NinjaMailerJob implements ShouldQueue
*/ */
private function preFlightChecksFail(): bool private function preFlightChecksFail(): bool
{ {
/* Always send regardless */
if($this->override) {
return false;
}
/* If we are migrating data we don't want to fire any emails */ /* If we are migrating data we don't want to fire any emails */
if ($this->company->is_disabled && !$this->override) { if ($this->company->is_disabled) {
return true; return true;
} }

View File

@ -550,7 +550,7 @@ class Account extends BaseModel
$limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 50; $limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 50;
} else { } else {
$limit = $this->free_plan_email_quota; $limit = $this->free_plan_email_quota;
$limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 10; $limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 3;
} }
return min($limit, 5000); return min($limit, 5000);

View File

@ -190,7 +190,7 @@ class BaseRepository
$this->new_model = true; $this->new_model = true;
if (is_array($model->line_items) && !($model instanceof RecurringInvoice)) { if (is_array($model->line_items) && !($model instanceof RecurringInvoice)) {
$model->line_items = (collect($model->line_items))->map(function ($item) use ($model, $client) { $model->line_items = (collect($model->line_items))->map(function ($item) use ($client) {
$item->notes = Helpers::processReservedKeywords($item->notes, $client); $item->notes = Helpers::processReservedKeywords($item->notes, $client);
return $item; return $item;

View File

@ -247,7 +247,7 @@ class Email implements ShouldQueue
$mailer->send($this->mailable); $mailer->send($this->mailable);
Cache::increment($this->company->account->key); Cache::increment("email_quota".$this->company->account->key);
LightLogs::create(new EmailSuccess($this->company->company_key)) LightLogs::create(new EmailSuccess($this->company->company_key))
->send(); ->send();
@ -329,8 +329,13 @@ class Email implements ShouldQueue
*/ */
public function preFlightChecksFail(): bool public function preFlightChecksFail(): bool
{ {
/* Always send if disabled */
if($this->override) {
return false;
}
/* If we are migrating data we don't want to fire any emails */ /* If we are migrating data we don't want to fire any emails */
if ($this->company->is_disabled && !$this->override) { if ($this->company->is_disabled) {
return true; return true;
} }

View File

@ -95,7 +95,7 @@ class EmailMailer implements ShouldQueue
$mailer->send($this->email_mailable); $mailer->send($this->email_mailable);
Cache::increment($this->email_service->company->account->key); Cache::increment("email_quota".$this->email_service->company->account->key);
LightLogs::create(new EmailSuccess($this->email_service->company->company_key)) LightLogs::create(new EmailSuccess($this->email_service->company->company_key))
->send(); ->send();

View File

@ -0,0 +1,140 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Services\Invoice\EInvoice;
use SimpleXMLElement;
use App\Models\Invoice;
use App\Services\AbstractService;
/*
<?xml version="1.0" encoding="UTF-8"?>
<FatturaElettronica versione="FPR12" xmlns="http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2">
<FatturaElettronicaHeader>
<DatiTrasmissione>
<IdTrasmittente>
<IdPaese>IT</IdPaese>
<IdCodice>01234567890</IdCodice>
</IdTrasmittente>
<ProgressivoInvio>00001</ProgressivoInvio>
<FormatoTrasmissione>FPR12</FormatoTrasmissione>
<CodiceDestinatario>ABCDE1</CodiceDestinatario>
</DatiTrasmissione>
<CedentePrestatore>
<!-- Company information of the sender (seller/provider) -->
</CedentePrestatore>
<CessionarioCommittente>
<!-- Company information of the receiver (buyer) -->
</CessionarioCommittente>
</FatturaElettronicaHeader>
<FatturaElettronicaBody>
<DatiGenerali>
<DatiGeneraliDocumento>
<TipoDocumento>TD01</TipoDocumento>
<Divisa>EUR</Divisa>
<Data>2023-04-21</Data>
<Numero>1</Numero>
<!-- Add other information as needed -->
</DatiGeneraliDocumento>
<!-- Add other general data as needed -->
</DatiGenerali>
<DatiBeniServizi>
<!-- List of items or services -->
</DatiBeniServizi>
<DatiPagamento>
<!-- Payment details -->
</DatiPagamento>
</FatturaElettronicaBody>
</FatturaElettronica>
*/
class FatturaPA extends AbstractService
{
private $xml;
public function __construct(public Invoice $invoice)
{
$this->xml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><FatturaElettronica></FatturaElettronica>');
}
public function run()
{
return $this->addHeader()->getXml();
}
public function addHeader() {
$this->xml->addChild('FatturaElettronicaHeader');
return $this;
}
public function addTrasmissioneData($idPaese, $idCodice, $progressivoInvio, $formatoTrasmissione, $codiceDestinatario) {
$datiTrasmissione = $this->xml->FatturaElettronicaHeader->addChild('DatiTrasmissione');
$idTrasmittente = $datiTrasmissione->addChild('IdTrasmittente');
$idTrasmittente->addChild('IdPaese', $idPaese);
$idTrasmittente->addChild('IdCodice', $idCodice);
$datiTrasmissione->addChild('ProgressivoInvio', $progressivoInvio);
$datiTrasmissione->addChild('FormatoTrasmissione', $formatoTrasmissione);
$datiTrasmissione->addChild('CodiceDestinatario', $codiceDestinatario);
return $this;
}
public function addCedentePrestatore($data) {
// Add CedentePrestatore data
}
public function addCessionarioCommittente($data) {
// Add CessionarioCommittente data
}
public function addBody() {
$this->xml->addChild('FatturaElettronicaBody');
return $this;
}
public function addDatiGenerali($data) {
// Add DatiGenerali data
}
public function addLineItem($data) {
if (!isset($this->xml->FatturaElettronicaBody->DatiBeniServizi)) {
$this->xml->FatturaElettronicaBody->addChild('DatiBeniServizi');
}
$lineItem = $this->xml->FatturaElettronicaBody->DatiBeniServizi->addChild('DettaglioLinee');
$lineItem->addChild('NumeroLinea', $data['NumeroLinea']);
$lineItem->addChild('Descrizione', $data['notes']);
$lineItem->addChild('Quantita', $data['quantity']);
$lineItem->addChild('PrezzoUnitario', $data['cost']);
$lineItem->addChild('PrezzoTotale', $data['line_total']);
$lineItem->addChild('AliquotaIVA', $data['tax_rate1']);
if (isset($data['UnitaMisura'])) {
$lineItem->addChild('UnitaMisura', $data['UnitaMisura']);
}
return $this;
}
public function addDatiPagamento($data) {
// Add DatiPagamento data
}
public function getXml()
{
return $this->xml->asXML();
}
}
// $fattura = new FatturaPA();
// $fattura
// ->addHeader()
// ->addTrasmissioneData('IT', '01234567890', '00001', 'FPR12', 'ABCDE1');
// echo $fattura->getXml();

View File

@ -28,7 +28,7 @@ class EmailStats
*/ */
public static function inc($company_key) public static function inc($company_key)
{ {
Cache::increment(self::EMAIL.$company_key); Cache::increment("email_quota".self::EMAIL.$company_key);
} }
/** /**

View File

@ -155,7 +155,7 @@ class HtmlEngine
$data['$exchange_rate'] = ['value' => $this->entity->exchange_rate ?: ' ', 'label' => ctrans('texts.exchange_rate')]; $data['$exchange_rate'] = ['value' => $this->entity->exchange_rate ?: ' ', 'label' => ctrans('texts.exchange_rate')];
if ($this->entity_string == 'invoice' || $this->entity_string == 'recurring_invoice') { if ($this->entity_string == 'invoice' || $this->entity_string == 'recurring_invoice') {
$data['$entity'] = ['value' => '', 'label' => ctrans('texts.invoice')]; $data['$entity'] = ['value' => ctrans('texts.invoice'), 'label' => ctrans('texts.invoice')];
$data['$number'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number')]; $data['$number'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number')];
$data['$invoice'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number')]; $data['$invoice'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number')];
$data['$number_short'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number_short')]; $data['$number_short'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number_short')];
@ -212,7 +212,7 @@ class HtmlEngine
} }
if ($this->entity_string == 'quote') { if ($this->entity_string == 'quote') {
$data['$entity'] = ['value' => '', 'label' => ctrans('texts.quote')]; $data['$entity'] = ['value' => ctrans('texts.quote'), 'label' => ctrans('texts.quote')];
$data['$number'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.quote_number')]; $data['$number'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.quote_number')];
$data['$number_short'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.quote_number_short')]; $data['$number_short'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.quote_number_short')];
$data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms ?: ''), $this->client) ?: '', 'label' => ctrans('texts.quote_terms')]; $data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms ?: ''), $this->client) ?: '', 'label' => ctrans('texts.quote_terms')];
@ -256,7 +256,7 @@ class HtmlEngine
} }
if ($this->entity_string == 'credit') { if ($this->entity_string == 'credit') {
$data['$entity'] = ['value' => '', 'label' => ctrans('texts.credit')]; $data['$entity'] = ['value' => ctrans('texts.credit'), 'label' => ctrans('texts.credit')];
$data['$number'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.credit_number')]; $data['$number'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.credit_number')];
$data['$number_short'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.credit_number_short')]; $data['$number_short'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.credit_number_short')];
$data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms ?: ''), $this->client) ?: '', 'label' => ctrans('texts.credit_terms')]; $data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms ?: ''), $this->client) ?: '', 'label' => ctrans('texts.credit_terms')];

View File

@ -4379,7 +4379,7 @@ Una vez que tenga los montos, vuelva a esta página de métodos de pago y haga c
'imported_customers' => 'Se comenzó a importar clientes con éxito', 'imported_customers' => 'Se comenzó a importar clientes con éxito',
'login_success' => 'Inicio de sesión correcto', 'login_success' => 'Inicio de sesión correcto',
'login_failure' => 'Inicio de sesión fallido', 'login_failure' => 'Inicio de sesión fallido',
'exported_data' => 'Once the file is ready you\'ll receive an email with a download link', 'exported_data' => 'Una vez que el archivo esté listo, recibirá un correo electrónico con un enlace de descarga.',
'include_deleted_clients' => 'Incluir clientes eliminados', 'include_deleted_clients' => 'Incluir clientes eliminados',
'include_deleted_clients_help' => 'Cargar registros pertenecientes a clientes eliminados', 'include_deleted_clients_help' => 'Cargar registros pertenecientes a clientes eliminados',
'step_1_sign_in' => 'Paso 1: Iniciar sesión', 'step_1_sign_in' => 'Paso 1: Iniciar sesión',
@ -4468,7 +4468,7 @@ Una vez que tenga los montos, vuelva a esta página de métodos de pago y haga c
'activity_123' => ':user eliminó el gasto recurrente :recurring_expense', 'activity_123' => ':user eliminó el gasto recurrente :recurring_expense',
'activity_124' => ':user restauró el gasto recurrente :recurring_expense', 'activity_124' => ':user restauró el gasto recurrente :recurring_expense',
'fpx' => "FPX", 'fpx' => "FPX",
'to_view_entity_set_password' => 'To view the :entity you need to set a password.', 'to_view_entity_set_password' => 'Para ver el :entity necesita establecer una contraseña.',
'unsubscribe' => 'Darse de baja', 'unsubscribe' => 'Darse de baja',
'unsubscribed' => 'Dado de baja', 'unsubscribed' => 'Dado de baja',
'unsubscribed_text' => 'Se le ha eliminado de las notificaciones de este documento', 'unsubscribed_text' => 'Se le ha eliminado de las notificaciones de este documento',
@ -4566,7 +4566,7 @@ Una vez que tenga los montos, vuelva a esta página de métodos de pago y haga c
'purchase_order_number' => 'Número de orden de compra', 'purchase_order_number' => 'Número de orden de compra',
'purchase_order_number_short' => 'orden de compra n.°', 'purchase_order_number_short' => 'orden de compra n.°',
'inventory_notification_subject' => 'Notificación de umbral de inventario para el producto: :product', 'inventory_notification_subject' => 'Notificación de umbral de inventario para el producto: :product',
'inventory_notification_body' => 'Threshold of :amount has been reached for product: :product', 'inventory_notification_body' => 'Se ha alcanzado el umbral de:amount para el producto: :product',
'activity_130' => ':user creó la orden de compra :purchase_order', 'activity_130' => ':user creó la orden de compra :purchase_order',
'activity_131' => ':user actualizó la orden de compra :purchase_order', 'activity_131' => ':user actualizó la orden de compra :purchase_order',
'activity_132' => ':user archivó la orden de compra :purchase_order', 'activity_132' => ':user archivó la orden de compra :purchase_order',
@ -4598,7 +4598,7 @@ Una vez que tenga los montos, vuelva a esta página de métodos de pago y haga c
'vendor_document_upload' => 'Carga de documentos de proveedores', 'vendor_document_upload' => 'Carga de documentos de proveedores',
'vendor_document_upload_help' => 'Permitir que los proveedores carguen documentos', 'vendor_document_upload_help' => 'Permitir que los proveedores carguen documentos',
'are_you_enjoying_the_app' => '¿Estás disfrutando de la aplicación?', 'are_you_enjoying_the_app' => '¿Estás disfrutando de la aplicación?',
'yes_its_great' => 'Yes, it\'s great!', 'yes_its_great' => '¡Sí, es genial!',
'not_so_much' => 'No tanto', 'not_so_much' => 'No tanto',
'would_you_rate_it' => '¡Me alegro de oirlo! ¿Te gustaría calificarlo?', 'would_you_rate_it' => '¡Me alegro de oirlo! ¿Te gustaría calificarlo?',
'would_you_tell_us_more' => '¡Siento escucharlo! ¿Te gustaría contarnos más?', 'would_you_tell_us_more' => '¡Siento escucharlo! ¿Te gustaría contarnos más?',
@ -4969,9 +4969,9 @@ Una vez que tenga los montos, vuelva a esta página de métodos de pago y haga c
'white_label_body' => 'Gracias por comprar una licencia de marca blanca.<br><br> Su clave de licencia es:<br><br> :license_key', 'white_label_body' => 'Gracias por comprar una licencia de marca blanca.<br><br> Su clave de licencia es:<br><br> :license_key',
'payment_type_Klarna' => 'Klarna', 'payment_type_Klarna' => 'Klarna',
'payment_type_Interac E Transfer' => 'Interac E Transfer', 'payment_type_Interac E Transfer' => 'Interac E Transfer',
'xinvoice_payable' => 'Payable within :payeddue days net until :paydate', 'xinvoice_payable' => 'Pagadero dentro de :payeddue días de pago vencido neto hasta :paydate',
'xinvoice_no_buyers_reference' => "No buyer's reference given", 'xinvoice_no_buyers_reference' => "No se da la referencia del comprador",
'xinvoice_online_payment' => 'The invoice needs to be payed online via the provided link', 'xinvoice_online_payment' => 'La factura debe pagarse en línea a través del enlace proporcionado.',
'pre_payment' => 'Prepago', 'pre_payment' => 'Prepago',
'number_of_payments' => 'Numero de pagos', 'number_of_payments' => 'Numero de pagos',
'number_of_payments_helper' => 'El número de veces que se realizará este pago.', 'number_of_payments_helper' => 'El número de veces que se realizará este pago.',
@ -5034,24 +5034,24 @@ Una vez que tenga los montos, vuelva a esta página de métodos de pago y haga c
'taxable_amount' => 'Base imponible', 'taxable_amount' => 'Base imponible',
'tax_summary' => 'Resumen de impuestos', 'tax_summary' => 'Resumen de impuestos',
'oauth_mail' => 'OAuth / Mail', 'oauth_mail' => 'OAuth / Mail',
'preferences' => 'Preferences', 'preferences' => 'Preferencias',
'analytics' => 'Analytics', 'analytics' => 'Analítica',
'reduced_rate' => 'Reduced Rate', 'reduced_rate' => 'Tarifa Reducida',
'tax_all' => 'Tax All', 'tax_all' => 'Impuestos Todos',
'tax_selected' => 'Tax Selected', 'tax_selected' => 'Impuesto Seleccionado',
'version' => 'version', 'version' => 'versión',
'seller_subregion' => 'Seller Subregion', 'seller_subregion' => 'Subregión del vendedor',
'calculate_taxes' => 'Calculate Taxes', 'calculate_taxes' => 'Calcular impuestos',
'calculate_taxes_help' => 'Automatically calculate taxes when saving invoices', 'calculate_taxes_help' => 'Calcular automáticamente los impuestos al guardar las facturas',
'link_expenses' => 'Link Expenses', 'link_expenses' => 'Gastos de enlace',
'converted_client_balance' => 'Converted Client Balance', 'converted_client_balance' => 'Saldo de cliente convertido',
'converted_payment_balance' => 'Converted Payment Balance', 'converted_payment_balance' => 'Saldo de pago convertido',
'total_hours' => 'Total Hours', 'total_hours' => 'Horas totales',
'date_picker_hint' => 'Use +days to set the date in the future', 'date_picker_hint' => 'Usa +días para establecer la fecha en el futuro',
'app_help_link' => 'More information ', 'app_help_link' => 'Más información',
'here' => 'here', 'here' => 'aquí',
'industry_Restaurant & Catering' => 'Restaurant & Catering', 'industry_Restaurant & Catering' => 'Restaurante y Catering',
'show_credits_table' => 'Show Credits Table', 'show_credits_table' => 'Mostrar tabla de créditos',
); );

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -14,11 +14,11 @@
@if($invoice->isPayable() && $client->getSetting('custom_message_unpaid_invoice')) @if($invoice->isPayable() && $client->getSetting('custom_message_unpaid_invoice'))
@component('portal.ninja2020.components.message') @component('portal.ninja2020.components.message')
{{ $client->getSetting('custom_message_unpaid_invoice') }} <pre>{{ $client->getSetting('custom_message_unpaid_invoice') }}</pre>
@endcomponent @endcomponent
@elseif($invoice->status_id === 4 && $client->getSetting('custom_message_paid_invoice')) @elseif($invoice->status_id === 4 && $client->getSetting('custom_message_paid_invoice'))
@component('portal.ninja2020.components.message') @component('portal.ninja2020.components.message')
{{ $client->getSetting('custom_message_paid_invoice') }} <pre>{{ $client->getSetting('custom_message_paid_invoice') }}</pre>
@endcomponent @endcomponent
@endif @endif

View File

@ -18,7 +18,7 @@
@if(!$quote->isApproved() && $client->getSetting('custom_message_unapproved_quote')) @if(!$quote->isApproved() && $client->getSetting('custom_message_unapproved_quote'))
@component('portal.ninja2020.components.message') @component('portal.ninja2020.components.message')
{{ $client->getSetting('custom_message_unapproved_quote') }} <pre>{{ $client->getSetting('custom_message_unapproved_quote') }}</pre>
@endcomponent @endcomponent
@endif @endif

View File

@ -0,0 +1,48 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace Tests\Feature;
use App\Services\Invoice\EInvoice\FatturaPA;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Tests\MockAccountData;
use Tests\TestCase;
/**
* @test
*/
class FatturaPATest extends TestCase
{
use DatabaseTransactions;
use MockAccountData;
protected function setUp(): void
{
parent::setUp();
$this->makeTestData();
$this->withoutMiddleware(
ThrottleRequests::class
);
}
public function testInvoiceBoot()
{
$fat = new FatturaPA($this->invoice);
$xml = $fat->run();
// nlog($xml);
$this->assertnotNull($xml);
}
}

View File

@ -579,7 +579,7 @@ class RecurringInvoiceTest extends TestCase
'user_id' => $this->user->id, 'user_id' => $this->user->id,
'cost' => 10, 'cost' => 10,
'price' => 10, 'price' => 10,
'product_key' => $this->faker->word, 'product_key' => $this->faker->unique()->word(),
]); ]);
$p2 = Product::factory()->create([ $p2 = Product::factory()->create([
@ -587,7 +587,7 @@ class RecurringInvoiceTest extends TestCase
'user_id' => $this->user->id, 'user_id' => $this->user->id,
'cost' => 20, 'cost' => 20,
'price' => 20, 'price' => 20,
'product_key' => $this->faker->word, 'product_key' => $this->faker->unique()->word(),
]); ]);
$recurring_invoice = RecurringInvoiceFactory::create($this->company->id, $this->user->id); $recurring_invoice = RecurringInvoiceFactory::create($this->company->id, $this->user->id);
@ -627,7 +627,7 @@ class RecurringInvoiceTest extends TestCase
$response = $this->withHeaders([ $response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'), 'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->get('/api/v1/recurring_invoices?product_key=' . $this->faker->word) ])->get('/api/v1/recurring_invoices?product_key=' . $this->faker->unique()->word())
->assertStatus(200); ->assertStatus(200);
$arr = $response->json(); $arr = $response->json();

View File

@ -11,18 +11,18 @@
namespace Tests\Unit\Tax; namespace Tests\Unit\Tax;
use Tests\TestCase; use App\DataMapper\CompanySettings;
use App\DataMapper\Tax\DE\Rule;
use App\DataMapper\Tax\TaxModel;
use App\DataMapper\Tax\ZipTax\Response;
use App\Models\Client; use App\Models\Client;
use App\Models\Company; use App\Models\Company;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Product; use App\Models\Product;
use Tests\MockAccountData;
use App\DataMapper\Tax\DE\Rule;
use App\DataMapper\Tax\TaxModel;
use App\DataMapper\CompanySettings;
use App\DataMapper\Tax\ZipTax\Response;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Tests\MockAccountData;
use Tests\TestCase;
/** /**
* @test App\Services\Tax\Providers\EuTax * @test App\Services\Tax\Providers\EuTax
@ -338,8 +338,18 @@ class EuTaxTest extends TestCase
'has_valid_vat_number' => false, 'has_valid_vat_number' => false,
]); ]);
$invoice = Invoice::factory()->create([
'company_id' => $company->id,
'client_id' => $client->id,
'user_id' => $this->user->id,
'status_id' => Invoice::STATUS_SENT,
'tax_data' => new Response([
'geoState' => 'CA',
]),
]);
$process = new Rule(); $process = new Rule();
$process->setClient($client); $process->setInvoice($invoice);
$process->init(); $process->init();
$this->assertEquals('EU', $process->seller_region); $this->assertEquals('EU', $process->seller_region);
@ -382,12 +392,21 @@ class EuTaxTest extends TestCase
'has_valid_vat_number' => false, 'has_valid_vat_number' => false,
]); ]);
$invoice = Invoice::factory()->create([
'company_id' => $company->id,
'client_id' => $client->id,
'user_id' => $this->user->id,
'status_id' => Invoice::STATUS_SENT,
'tax_data' => new Response([
'geoState' => 'CA',
]),
]);
$process = new Rule(); $process = new Rule();
$process->setClient($client); $process->setInvoice($invoice);
$process->init(); $process->init();
$this->assertEquals('EU', $process->seller_region);
$this->assertEquals('EU', $process->seller_region);
$this->assertEquals('BE', $process->client_subregion); $this->assertEquals('BE', $process->client_subregion);
@ -428,11 +447,18 @@ $this->assertEquals('EU', $process->seller_region);
'has_valid_vat_number' => false, 'has_valid_vat_number' => false,
]); ]);
$invoice = Invoice::factory()->create([
'company_id' => $company->id,
'client_id' => $client->id,
'user_id' => $this->user->id,
'status_id' => Invoice::STATUS_SENT,
'tax_data' => new Response([
'geoState' => 'CA',
]),
]);
$process = new Rule(); $process = new Rule();
$process->setTaxData(new Response([ $process->setInvoice($invoice);
'geoState' => 'CA',
]));
$process->setClient($client);
$process->init(); $process->init();
$this->assertEquals('EU', $process->seller_region); $this->assertEquals('EU', $process->seller_region);
@ -476,8 +502,18 @@ $this->assertEquals('EU', $process->seller_region);
'has_valid_vat_number' => false, 'has_valid_vat_number' => false,
]); ]);
$invoice = Invoice::factory()->create([
'company_id' => $company->id,
'client_id' => $client->id,
'user_id' => $this->user->id,
'status_id' => Invoice::STATUS_SENT,
'tax_data' => new Response([
'geoState' => 'CA',
]),
]);
$process = new Rule(); $process = new Rule();
$process->setClient($client); $process->setInvoice($invoice);
$process->init(); $process->init();
$this->assertInstanceOf(Rule::class, $process); $this->assertInstanceOf(Rule::class, $process);
@ -517,10 +553,21 @@ $this->assertEquals('EU', $process->seller_region);
'has_valid_vat_number' => true, 'has_valid_vat_number' => true,
]); ]);
$invoice = Invoice::factory()->create([
'company_id' => $company->id,
'client_id' => $client->id,
'user_id' => $this->user->id,
'status_id' => Invoice::STATUS_SENT,
'tax_data' => new Response([
'geoState' => 'CA',
]),
]);
$process = new Rule(); $process = new Rule();
$process->setClient($client); $process->setInvoice($invoice);
$process->init(); $process->init();
$this->assertInstanceOf(Rule::class, $process); $this->assertInstanceOf(Rule::class, $process);
$this->assertTrue($client->has_valid_vat_number); $this->assertTrue($client->has_valid_vat_number);
@ -556,11 +603,22 @@ $this->assertEquals('EU', $process->seller_region);
'shipping_country_id' => 56, 'shipping_country_id' => 56,
'has_valid_vat_number' => true, 'has_valid_vat_number' => true,
]); ]);
$invoice = Invoice::factory()->create([
'company_id' => $company->id,
'client_id' => $client->id,
'user_id' => $this->user->id,
'status_id' => Invoice::STATUS_SENT,
'tax_data' => new Response([
'geoState' => 'CA',
]),
]);
$process = new Rule(); $process = new Rule();
$process->setClient($client); $process->setInvoice($invoice);
$process->init(); $process->init();
$this->assertInstanceOf(Rule::class, $process); $this->assertInstanceOf(Rule::class, $process);
$this->assertTrue($client->has_valid_vat_number); $this->assertTrue($client->has_valid_vat_number);
@ -597,10 +655,21 @@ $this->assertEquals('EU', $process->seller_region);
'is_tax_exempt' => true, 'is_tax_exempt' => true,
]); ]);
$invoice = Invoice::factory()->create([
'company_id' => $company->id,
'client_id' => $client->id,
'user_id' => $this->user->id,
'status_id' => Invoice::STATUS_SENT,
'tax_data' => new Response([
'geoState' => 'CA',
]),
]);
$process = new Rule(); $process = new Rule();
$process->setClient($client); $process->setInvoice($invoice);
$process->init(); $process->init();
$this->assertInstanceOf(Rule::class, $process); $this->assertInstanceOf(Rule::class, $process);
$this->assertTrue($client->is_tax_exempt); $this->assertTrue($client->is_tax_exempt);
@ -637,8 +706,18 @@ $this->assertEquals('EU', $process->seller_region);
'is_tax_exempt' => true, 'is_tax_exempt' => true,
]); ]);
$invoice = Invoice::factory()->create([
'company_id' => $company->id,
'client_id' => $client->id,
'user_id' => $this->user->id,
'status_id' => Invoice::STATUS_SENT,
'tax_data' => new Response([
'geoState' => 'CA',
]),
]);
$process = new Rule(); $process = new Rule();
$process->setClient($client); $process->setInvoice($invoice);
$process->init(); $process->init();
$this->assertInstanceOf(Rule::class, $process); $this->assertInstanceOf(Rule::class, $process);
@ -676,11 +755,21 @@ $this->assertEquals('EU', $process->seller_region);
'is_tax_exempt' => true, 'is_tax_exempt' => true,
]); ]);
$invoice = Invoice::factory()->create([
'company_id' => $company->id,
'client_id' => $client->id,
'user_id' => $this->user->id,
'status_id' => Invoice::STATUS_SENT,
'tax_data' => new Response([
'geoState' => 'CA',
]),
]);
$process = new Rule(); $process = new Rule();
$process->setTaxData(new Response([])); $process->setInvoice($invoice);
$process->setClient($client);
$process->init(); $process->init();
$this->assertInstanceOf(Rule::class, $process); $this->assertInstanceOf(Rule::class, $process);
$this->assertTrue($client->is_tax_exempt); $this->assertTrue($client->is_tax_exempt);