diff --git a/VERSION.txt b/VERSION.txt index cdaeb5eb133b..9c4d53bf2eeb 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.10.19 \ No newline at end of file +5.10.20 \ No newline at end of file diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index 78eb1bd9445b..4448c7525898 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -215,7 +215,7 @@ class BaseRule implements RuleInterface $this->invoice->tax_data = $tax_data; - if(\DB::transactionLevel() == 0) { + if(\DB::transactionLevel() == 0 && isset($this->invoice->id)) { try { $this->invoice->saveQuietly(); diff --git a/app/DataMapper/Tax/DE/Rule.php b/app/DataMapper/Tax/DE/Rule.php index 726043cf5aa3..34d63bffefa9 100644 --- a/app/DataMapper/Tax/DE/Rule.php +++ b/app/DataMapper/Tax/DE/Rule.php @@ -43,6 +43,8 @@ class Rule extends BaseRule implements RuleInterface public float $reduced_tax_rate = 0; public string $tax_name1 = 'MwSt.'; + + private string $tax_name; /** * Initializes the rules and builds any required data. * @@ -50,6 +52,7 @@ class Rule extends BaseRule implements RuleInterface */ public function init(): self { + $this->tax_name = $this->tax_name1; $this->calculateRates(); return $this; @@ -91,6 +94,7 @@ class Rule extends BaseRule implements RuleInterface */ public function reverseTax($item): self { + $this->tax_name1 = $this->tax_name; $this->tax_rate1 = 0; return $this; @@ -103,6 +107,8 @@ class Rule extends BaseRule implements RuleInterface */ public function taxReduced($item): self { + + $this->tax_name1 = $this->tax_name; $this->tax_rate1 = $this->reduced_tax_rate; return $this; @@ -115,6 +121,8 @@ class Rule extends BaseRule implements RuleInterface */ public function zeroRated($item): self { + + $this->tax_name1 = $this->tax_name; $this->tax_rate1 = 0; return $this; @@ -142,6 +150,7 @@ class Rule extends BaseRule implements RuleInterface public function taxDigital($item): self { + $this->tax_name1 = $this->tax_name; $this->tax_rate1 = $this->tax_rate; return $this; @@ -155,6 +164,7 @@ class Rule extends BaseRule implements RuleInterface public function taxService($item): self { + $this->tax_name1 = $this->tax_name; $this->tax_rate1 = $this->tax_rate; return $this; @@ -168,6 +178,7 @@ class Rule extends BaseRule implements RuleInterface public function taxShipping($item): self { + $this->tax_name1 = $this->tax_name; $this->tax_rate1 = $this->tax_rate; return $this; @@ -181,6 +192,7 @@ class Rule extends BaseRule implements RuleInterface public function taxPhysical($item): self { + $this->tax_name1 = $this->tax_name; $this->tax_rate1 = $this->tax_rate; return $this; diff --git a/app/Filters/ExpenseFilters.php b/app/Filters/ExpenseFilters.php index ca2b1b09bdde..2fa80a1eb799 100644 --- a/app/Filters/ExpenseFilters.php +++ b/app/Filters/ExpenseFilters.php @@ -158,6 +158,18 @@ class ExpenseFilters extends QueryFilters return $this->builder; } + public function categories(string $categories = ''): Builder + { + $categories_exploded = explode(",", $categories); + + if(empty($categories) || count(array_filter($categories_exploded)) == 0) + return $this->builder; + + $categories_keys = $this->transformKeys($categories_exploded); + + return $this->builder->whereIn('category_id', $categories_keys); + } + public function number(string $number = ''): Builder { if (strlen($number) == 0) { diff --git a/app/Filters/TaskFilters.php b/app/Filters/TaskFilters.php index 3b26e004742b..c179bb44ab77 100644 --- a/app/Filters/TaskFilters.php +++ b/app/Filters/TaskFilters.php @@ -85,6 +85,10 @@ class TaskFilters extends QueryFilters $this->builder->whereNull('invoice_id'); } + if (in_array('is_running', $status_parameters)) { + $this->builder->where('is_running', true); + } + return $this->builder; } diff --git a/app/Http/ViewComposers/Components/Rotessa/ContactComponent.php b/app/Http/ViewComposers/Components/Rotessa/ContactComponent.php index f1b0c0adf165..dcaff72ab283 100644 --- a/app/Http/ViewComposers/Components/Rotessa/ContactComponent.php +++ b/app/Http/ViewComposers/Components/Rotessa/ContactComponent.php @@ -53,7 +53,6 @@ class ContactComponent extends Component public function render() { - \Debugbar::debug($this->attributes->getAttributes() + $this->defaults); return render('gateways.rotessa.components.contact', $this->attributes->getAttributes() + $this->defaults ); } } diff --git a/app/PaymentDrivers/PayPal/PayPalBasePaymentDriver.php b/app/PaymentDrivers/PayPal/PayPalBasePaymentDriver.php index e7f6fe01c145..375b9c7420b7 100644 --- a/app/PaymentDrivers/PayPal/PayPalBasePaymentDriver.php +++ b/app/PaymentDrivers/PayPal/PayPalBasePaymentDriver.php @@ -115,10 +115,9 @@ class PayPalBasePaymentDriver extends BaseDriver $this->api_endpoint_url = $this->company_gateway->getConfigField('testMode') ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com'; - if(\App\Utils\Ninja::isHosted()) { + if(\App\Utils\Ninja::isHosted() && $this->company_gateway->gateway_key != '80af24a6a691230bbec33e930ab40665') { $secret = config('ninja.paypal.secret'); $client_id = config('ninja.paypal.client_id'); - } else { diff --git a/app/Repositories/TaskRepository.php b/app/Repositories/TaskRepository.php index ec96ed0670b0..96ee297427e9 100644 --- a/app/Repositories/TaskRepository.php +++ b/app/Repositories/TaskRepository.php @@ -147,6 +147,10 @@ class TaskRepository extends BaseRepository $task->calculated_start_date = $this->harvestStartDate($time_log, $task); + if(isset(end($time_log)[1])){ + $task->is_running = end($time_log)[1] == 0; + } + $task->time_log = json_encode($time_log); $task->saveQuietly(); @@ -259,7 +263,7 @@ class TaskRepository extends BaseRepository $log = array_merge($log, [$new]); $task->time_log = json_encode($log); - + $task->is_running = true; $task->saveQuietly(); } @@ -303,6 +307,7 @@ class TaskRepository extends BaseRepository $log = array_merge($log, [$last]);//check at this point, it may be prepending here. $task->time_log = json_encode($log); + $task->is_running = false; $task->saveQuietly(); } @@ -418,4 +423,4 @@ class TaskRepository extends BaseRepository } -} +} \ No newline at end of file diff --git a/app/Services/EDocument/Gateway/Storecove/Storecove.php b/app/Services/EDocument/Gateway/Storecove/Storecove.php index 0ce0a8561372..397e8813ff2b 100644 --- a/app/Services/EDocument/Gateway/Storecove/Storecove.php +++ b/app/Services/EDocument/Gateway/Storecove/Storecove.php @@ -137,14 +137,14 @@ class Storecove { } - public function sendDocument($document) + public function sendDocument(string $document, int $routing_id, array $identifiers = []) { $payload = [ - "legalEntityId"=> 290868, + "legalEntityId" => $routing_id, "idempotencyGuid"=> \Illuminate\Support\Str::uuid(), "routing" => [ - "eIdentifiers" => [], + "eIdentifiers" => $identifiers, "emails" => ["david@invoiceninja.com"] ], "document"=> [ diff --git a/app/Services/EDocument/Standards/Peppol.php b/app/Services/EDocument/Standards/Peppol.php index a0d7b4892f66..d6c6e8f538ee 100644 --- a/app/Services/EDocument/Standards/Peppol.php +++ b/app/Services/EDocument/Standards/Peppol.php @@ -57,6 +57,12 @@ class Peppol extends AbstractService use NumberFormatter; /** + * Assumptions: + * + * Line Item Taxes Only + * Exclusive Taxes + * + * * used as a proxy for * the schemeID of partyidentification * property - for Storecove only: @@ -152,6 +158,13 @@ class Peppol extends AbstractService private InvoiceSum | InvoiceSumInclusive $calc; private \InvoiceNinja\EInvoice\Models\Peppol\Invoice $p_invoice; + + private ?\InvoiceNinja\EInvoice\Models\Peppol\Invoice $_client_settings; + + private ?\InvoiceNinja\EInvoice\Models\Peppol\Invoice $_company_settings; + + private EInvoice $e; + /** * @param Invoice $invoice */ @@ -159,18 +172,21 @@ class Peppol extends AbstractService { $this->company = $invoice->company; $this->calc = $this->invoice->calc(); - $this->setInvoice(); + $this->e = new EInvoice(); + $this->setSettings()->setInvoice(); } - + + /** + * Rehydrates an existing e invoice - or - scaffolds a new one + * + * @return self + */ private function setInvoice(): self { - if($this->invoice->e_invoice){ - - $e = new EInvoice(); - $this->p_invoice = $e->decode('Peppol', json_encode($this->invoice->e_invoice->Invoice), 'json'); + $this->p_invoice = $this->e->decode('Peppol', json_encode($this->invoice->e_invoice->Invoice), 'json'); return $this; @@ -182,6 +198,21 @@ class Peppol extends AbstractService return $this; } + + /** + * Transforms the settings props into usable models we can merge. + * + * @return self + */ + private function setSettings(): self + { + $this->_client_settings = isset($this->invoice->client->e_invoice->Invoice) ? $this->e->decode('Peppol', json_encode($this->invoice->client->e_invoice->Invoice), 'json') : null; + + $this->_company_settings = isset($this->invoice->company->e_invoice->Invoice) ? $this->e->decode('Peppol', json_encode($this->invoice->company->e_invoice->Invoice), 'json') : null; + + return $this; + + } public function getInvoice(): \InvoiceNinja\EInvoice\Models\Peppol\Invoice { @@ -211,8 +242,7 @@ class Peppol extends AbstractService $json = $e->encode($this->p_invoice, 'json'); return $json; - // $prefixes = str_ireplace(["cac:","cbc:"], "", $json); - // return str_ireplace(["InvoiceLine", "PostalAddress", "PartyName"], ["invoiceLines","address", "companyName"], $prefixes); + } public function toArray(): array @@ -224,12 +254,16 @@ class Peppol extends AbstractService { $this->p_invoice->ID = $this->invoice->number; $this->p_invoice->IssueDate = new \DateTime($this->invoice->date); + + if($this->invoice->due_date) + $this->p_invoice->DueDate = new \DateTime($this->invoice->due_date); + $this->p_invoice->InvoiceTypeCode = 380; // $this->p_invoice->AccountingSupplierParty = $this->getAccountingSupplierParty(); $this->p_invoice->AccountingCustomerParty = $this->getAccountingCustomerParty(); $this->p_invoice->InvoiceLine = $this->getInvoiceLines(); - $this->p_invoice->TaxTotal = $this->getTotalTaxes(); + // $this->p_invoice->TaxTotal = $this->getTotalTaxes(); it only wants the aggregate here!! $this->p_invoice->LegalMonetaryTotal = $this->getLegalMonetaryTotal(); $this->countryLevelMutators(); @@ -340,7 +374,7 @@ class Peppol extends AbstractService $tax_total = new TaxTotal(); $tax_total->TaxAmount = $tax_amount; - $tax_total->TaxSubtotal = $tax_subtotal; + $tax_total->TaxSubtotal[] = $tax_subtotal; $taxes[] = $tax_total; @@ -372,7 +406,7 @@ class Peppol extends AbstractService $tax_total = new TaxTotal(); $tax_total->TaxAmount = $tax_amount; - $tax_total->TaxSubtotal = $tax_subtotal; + $tax_total->TaxSubtotal[] = $tax_subtotal; $taxes[] = $tax_total; @@ -739,13 +773,13 @@ class Peppol extends AbstractService public function getSetting(string $property_path): mixed { - if($prop_value = PropertyResolver::resolve($this->invoice->e_invoice, $property_path)) + if($prop_value = PropertyResolver::resolve($this->p_invoice, $property_path)) { return $prop_value; - elseif($prop_value = PropertyResolver::resolve($this->invoice->client->e_invoice, $property_path)) + }elseif($prop_value = PropertyResolver::resolve($this->_client_settings, $property_path)) { return $prop_value; - elseif($prop_value = PropertyResolver::resolve($this->invoice->company->e_invoice, $property_path)) + }elseif($prop_value = PropertyResolver::resolve($this->_company_settings, $property_path)) { return $prop_value; - + } return null; } @@ -761,10 +795,10 @@ class Peppol extends AbstractService private function setPaymentMeans(bool $required = false): self { - - if($this->p_invoice->PaymentMeans) + + if(isset($this->p_invoice->PaymentMeans)) return $this; - elseif(!isset($this->p_invoice->PaymentMeans) && $paymentMeans = $this->getSetting('Invoice.PaymentMeans')){ + elseif($paymentMeans = $this->getSetting('Invoice.PaymentMeans')){ $this->p_invoice->PaymentMeans = is_array($paymentMeans) ? $paymentMeans : [$paymentMeans]; return $this; } @@ -774,24 +808,48 @@ class Peppol extends AbstractService return $this; } - + + /** + * DE + * + * @Completed + * @Tested + * + * @return self + */ private function DE(): self { - // accountingsupplierparty.party.contact MUST be set - Name / Telephone / Electronic Mail - // this is forced by default. $this->setPaymentMeans(true); return $this; } - + + /** + * CH + * + * @Completed + * + * Completed - QR-Bill to be implemented at a later date. + * @return self + */ private function CH(): self { - //if QR-Bill support required - then special flow required.... optional. - return $this; } - + + /** + * AT + * + * @Pending + * + * Need to ensure when sending to government entities that we route appropriately + * Also need to ensure customerAssignedAccountIdValue is set so that the sender can be resolved. + * + * Need a way to define if the client is a government entity. + * + * @return self + */ private function AT(): self { //special fields for sending to AT:GOV @@ -804,14 +862,28 @@ class Peppol extends AbstractService //if payment means are included, they must be the same `type` return $this; } - + + /** + * ES + * + * @Pending + * + * ES:DIRE - routing identifier + * + * testing. //293098 + * + * @return self + */ private function ES(): self { - // For B2B, provide an ES:DIRE routing identifier and an ES:VAT tax identifier. - // both sender and receiver must be an ES company; - // you must have a "credit_transfer" PaymentMean; - // the "dueDate" property is mandatory. + if(!isset($this->invoice->due_date)) + $this->p_invoice->DueDate = new \DateTime($this->invoice->date); + + if($this->invoice->client->classification == 'business' && $this->invoice->company->getSetting('classification') == 'business') { + //must have a paymentmeans as credit_transfer + $this->setPaymentMeans(true); + } // For B2G, provide three ES:FACE identifiers in the routing object, // as well as the ES:VAT tax identifier in the accountingCustomerParty.publicIdentifiers. diff --git a/app/Services/Subscription/SubscriptionService.php b/app/Services/Subscription/SubscriptionService.php index af1ea653f64e..7ae780bf2409 100644 --- a/app/Services/Subscription/SubscriptionService.php +++ b/app/Services/Subscription/SubscriptionService.php @@ -1260,6 +1260,7 @@ class SubscriptionService return Subscription::query() ->where('company_id', $this->subscription->company_id) ->where('group_id', $this->subscription->group_id) + ->whereNotNull('group_id') ->where('id', '!=', $this->subscription->id) ->get(); } diff --git a/app/Utils/Traits/Pdf/PDF.php b/app/Utils/Traits/Pdf/PDF.php index b51b9914cd72..31d1e529c706 100644 --- a/app/Utils/Traits/Pdf/PDF.php +++ b/app/Utils/Traits/Pdf/PDF.php @@ -20,12 +20,18 @@ class PDF extends FPDI public function Footer() { $this->SetXY(0, -6); + $this->SetFont('Arial', 'I', 9); $this->SetTextColor(135, 135, 135); $trans = ctrans('texts.pdf_page_info', ['current' => $this->PageNo(), 'total' => '{nb}']); - // $trans = iconv('UTF-8', 'ISO-8859-7', $trans); + + try { + $trans = mb_convert_encoding($trans, 'ISO-8859-1', 'UTF-8'); + } + catch(\Exception $e){} + $this->Cell(0, 5, $trans, 0, 0, $this->text_alignment); } diff --git a/config/ninja.php b/config/ninja.php index 601b526f1601..1655b8a8832b 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -17,8 +17,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => env('APP_VERSION', '5.10.19'), - 'app_tag' => env('APP_TAG', '5.10.19'), + 'app_version' => env('APP_VERSION', '5.10.20'), + 'app_tag' => env('APP_TAG', '5.10.20'), 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', false), diff --git a/lang/en/texts.php b/lang/en/texts.php index 8a0abf6d6bd4..9df051a9cd38 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5311,6 +5311,7 @@ $lang = array( 'customer_type' => 'Customer Type', 'process_date' => 'Process Date', 'forever_free' => 'Forever Free', + 'comments_only' => 'Comments Only', ); return $lang; diff --git a/lang/es_ES/texts.php b/lang/es_ES/texts.php index 8bad35c312cb..a35db8b5f2e9 100644 --- a/lang/es_ES/texts.php +++ b/lang/es_ES/texts.php @@ -199,7 +199,7 @@ $lang = array( 'removed_logo' => 'Logo eliminado correctamente', 'sent_message' => 'Mensaje enviado correctamente', 'invoice_error' => 'Seleccionar cliente y corregir errores.', - 'limit_clients' => 'You\'ve hit the :count client limit on Free accounts. Congrats on your success!', + 'limit_clients' => 'Has alcanzado el límite de :count clientes en cuentas gratuitas. ¡Felicitaciones por tu éxito!', 'payment_error' => 'Ha habido un error en el proceso de tu Pago. Inténtalo de nuevo más tarde.', 'registration_required' => 'Se requiere registro', 'confirmation_required' => 'Por favor, confirma tu dirección de correo electrónico, :link para reenviar el email de confirmación.', @@ -1095,7 +1095,7 @@ $lang = array( 'invoice_embed_documents' => 'Documentos anexados', 'invoice_embed_documents_help' => 'Incluye imagenes adjuntas en la factura', 'document_email_attachment' => 'Adjuntar documentos', - 'ubl_email_attachment' => 'Attach UBL/E-Invoice', + 'ubl_email_attachment' => 'Adjuntar UBL/E-Invoice', 'download_documents' => 'Descargar documentos (:size)', 'documents_from_expenses' => 'De los Gastos:', 'dropzone_default_message' => 'Arrastra ficheros aquí o Haz clic para subir', @@ -2691,7 +2691,7 @@ Una vez que tenga los montos, vuelva a esta página de métodos de pago y haga c 'no_assets' => 'Sin imágenes, arrastra aquí para subir', 'add_image' => 'Añadir Imagen', 'select_image' => 'Seleccionar Imagen', - 'upgrade_to_upload_images' => 'Upgrade to the Enterprise Plan to upload files & images', + 'upgrade_to_upload_images' => 'Actualice al plan Enterprise para cargar archivos e imágenes', 'delete_image' => 'Borrar Imagen', 'delete_image_help' => 'Atención: borrar la imagen la eliminará de todas las propuestas.', 'amount_variable_help' => 'Nota: el campo de la factura $amount usará el campo parcial/depósito si se indica, de otra forma se usará el balance de la factura.', @@ -2909,13 +2909,13 @@ Una vez que tenga los montos, vuelva a esta página de métodos de pago y haga c 'mime_types' => 'Tipos de ficheros', 'mime_types_placeholder' => '.pdf , .docx, .jpg', 'mime_types_help' => 'Lista separada por comas de los tipos mime de fichero aceptados, déjalo en blanco para todos', - 'ticket_number_start_help' => 'Ticket number must be greater than the current ticket number', - 'new_ticket_template_id' => 'New ticket', - 'new_ticket_autoresponder_help' => 'Selecting a template will send an auto response to a client/contact when a new ticket is created', - 'update_ticket_template_id' => 'Updated ticket', - 'update_ticket_autoresponder_help' => 'Selecting a template will send an auto response to a client/contact when a ticket is updated', - 'close_ticket_template_id' => 'Closed ticket', - 'close_ticket_autoresponder_help' => 'Selecting a template will send an auto response to a client/contact when a ticket is closed', + 'ticket_number_start_help' => 'El número de ticket debe ser mayor que el número de ticket actual', + 'new_ticket_template_id' => 'Nuevo Ticket', + 'new_ticket_autoresponder_help' => 'Al seleccionar una plantilla se enviará una respuesta automática a un cliente/contacto cuando se cree un nuevo ticket.', + 'update_ticket_template_id' => 'Ticket actualizado', + 'update_ticket_autoresponder_help' => 'Al seleccionar una plantilla se enviará una respuesta automática a un cliente/contacto cuando se actualice un ticket.', + 'close_ticket_template_id' => 'Ticket cerrado', + 'close_ticket_autoresponder_help' => 'Al seleccionar una plantilla se enviará una respuesta automática a un cliente/contacto cuando se cierre un ticket.', 'default_priority' => 'Prioridad por defecto', 'alert_new_comment_id' => 'Nuevo comentario', 'update_ticket_notification_list' => 'Notificaciones de nuevo comentario adicionales', @@ -3041,7 +3041,7 @@ Una vez que tenga los montos, vuelva a esta página de métodos de pago y haga c 'portal_mode' => 'Modo portal', 'attach_pdf' => 'Adjuntar PDF', 'attach_documents' => 'Adjuntar Documentos', - 'attach_ubl' => 'Attach UBL/E-Invoice', + 'attach_ubl' => 'Adjuntar UBL/E-Invoice', 'email_style' => 'Estilo de correo electrónico', 'processed' => 'Procesado', 'fee_amount' => 'Importe de la cuota', @@ -3782,7 +3782,7 @@ Una vez que tenga los montos, vuelva a esta página de métodos de pago y haga c 'entity_number_placeholder' => ':entity # :entity_number', 'email_link_not_working' => 'Si el botón de arriba no te está funcionando, por favor pulsa en el enlace', 'display_log' => 'Mostrar Registro', - 'send_fail_logs_to_our_server' => 'Report errors to help improve the app', + 'send_fail_logs_to_our_server' => 'Informar errores para ayudar a mejorar la aplicación', 'setup' => 'Instalación', 'quick_overview_statistics' => 'Vistazo rápido y estadísticas', 'update_your_personal_info' => 'Actualiza tu información personal', @@ -4371,7 +4371,7 @@ Una vez que tenga los montos, vuelva a esta página de métodos de pago y haga c 'client_shipping_country' => 'País de envío del cliente', 'load_pdf' => 'Cargar PDF', 'start_free_trial' => 'Iniciar prueba gratuita', - 'start_free_trial_message' => 'Start your FREE 14 day trial of the Pro Plan', + 'start_free_trial_message' => 'Comience su prueba GRATUITA de 14 días del Plan Pro', 'due_on_receipt' => 'Adeudado a la recepción', 'is_paid' => 'Está pagado', 'age_group_paid' => 'Pagado', @@ -5082,7 +5082,7 @@ De lo contrario, este campo deberá dejarse en blanco.', 'payment_refund_receipt' => 'Recibo de reembolso de pago Nº :number', 'payment_receipt' => 'Recibo de pago Nº :number', 'load_template_description' => 'La plantilla se aplicará a lo siguiente:', - 'run_template' => 'Run Template', + 'run_template' => 'Ejecutar plantilla', 'statement_design' => 'Diseño de Estado de Cuenta', 'delivery_note_design' => 'Diseño de albarán de entrega', 'payment_receipt_design' => 'Diseño de recibo de pago', @@ -5121,7 +5121,7 @@ De lo contrario, este campo deberá dejarse en blanco.', 'all_contacts' => 'Todos los contactos', 'insert_below' => 'Insertar abajo', 'nordigen_handler_subtitle' => 'Autenticación de cuenta bancaria. Seleccionar su institución para completar la solicitud con las credenciales de su cuenta.', - 'nordigen_handler_error_heading_unknown' => 'Ha ocurrido un error', + 'nordigen_handler_error_heading_unknown' => 'Se ha producido un error', 'nordigen_handler_error_contents_unknown' => '¡Se ha producido un error desconocido! Razón:', 'nordigen_handler_error_heading_token_invalid' => 'Token no válido', 'nordigen_handler_error_contents_token_invalid' => 'El token proporcionado no era válido. Póngase en contacto con el soporte para obtener ayuda si este problema persiste.', @@ -5235,69 +5235,79 @@ De lo contrario, este campo deberá dejarse en blanco.', 'local_domain_help' => 'Dominio EHLO (opcional)', 'port_help' => 'Ej. 25.587.465', 'host_help' => 'Ej. smtp.gmail.com', - 'always_show_required_fields' => 'Allows show required fields form', - 'always_show_required_fields_help' => 'Displays the required fields form always at checkout', - 'advanced_cards' => 'Advanced Cards', - 'activity_140' => 'Statement sent to :client', - 'invoice_net_amount' => 'Invoice Net Amount', - 'round_to_minutes' => 'Round To Minutes', - '1_second' => '1 Second', - '1_minute' => '1 Minute', - '5_minutes' => '5 Minutes', - '15_minutes' => '15 Minutes', - '30_minutes' => '30 Minutes', - '1_hour' => '1 Hour', - '1_day' => '1 Day', - 'round_tasks' => 'Task Rounding Direction', - 'round_tasks_help' => 'Round task times up or down.', - 'direction' => 'Direction', - 'round_up' => 'Round Up', - 'round_down' => 'Round Down', - 'task_round_to_nearest' => 'Round To Nearest', - 'task_round_to_nearest_help' => 'The interval to round the task to.', - 'bulk_updated' => 'Successfully updated data', - 'bulk_update' => 'Bulk Update', - 'calculate' => 'Calculate', - 'sum' => 'Sum', - 'money' => 'Money', - 'web_app' => 'Web App', - 'desktop_app' => 'Desktop App', - 'disconnected' => 'Disconnected', - 'reconnect' => 'Reconnect', - 'e_invoice_settings' => 'E-Invoice Settings', - 'btcpay_refund_subject' => 'Refund of your invoice via BTCPay', - 'btcpay_refund_body' => 'A refund intended for you has been issued. To claim it via BTCPay, please click on this link:', + 'always_show_required_fields' => 'Mostrar siempre los campos obligatorios del formulario', + 'always_show_required_fields_help' => 'Muestra siempre el formulario de campos obligatorios al finalizar la compra', + 'advanced_cards' => 'Tarjetas avanzadas', + 'activity_140' => 'Declaración enviada a :client', + 'invoice_net_amount' => 'Importe neto de la factura', + 'round_to_minutes' => 'Redondear a minutos', + '1_second' => '1 segundo', + '1_minute' => '1 minuto', + '5_minutes' => '5 minutos', + '15_minutes' => '15 minutos', + '30_minutes' => '30 minutos', + '1_hour' => '1 hora', + '1_day' => '1 día', + 'round_tasks' => 'Dirección de redondeo de tareas', + 'round_tasks_help' => 'Redondea los tiempos de las tareas hacia arriba o hacia abajo.', + 'direction' => 'Dirección', + 'round_up' => 'Redondeo', + 'round_down' => 'Redondear a la baja', + 'task_round_to_nearest' => 'Redondear al más cercano', + 'task_round_to_nearest_help' => 'El intervalo al que se debe redondear la tarea.', + 'bulk_updated' => 'Datos actualizados con éxito', + 'bulk_update' => 'Actualización masiva', + 'calculate' => 'Calcular', + 'sum' => 'Suma', + 'money' => 'Dinero', + 'web_app' => 'Aplicación Web', + 'desktop_app' => 'Aplicación de escritorio', + 'disconnected' => 'Desconectado', + 'reconnect' => 'Reconectar', + 'e_invoice_settings' => 'Configuración de factura electrónica', + 'btcpay_refund_subject' => 'Reembolso de su factura a través de BTCPay', + 'btcpay_refund_body' => 'Se ha emitido un reembolso destinado a usted. Para reclamarlo a través de BTCPay, haga clic en este enlace:', 'currency_mauritanian_ouguiya' => 'Mauritanian Ouguiya', 'currency_bhutan_ngultrum' => 'Bhutan Ngultrum', - 'end_of_month' => 'End Of Month', - 'merge_e_invoice_to_pdf' => 'Merge E-Invoice and PDF', - 'task_assigned_subject' => 'New task assignment [Task :task] [ :date ]', - 'task_assigned_body' => 'You have been assigned task :task

Description: :description

Client: :client', - 'activity_141' => 'User :user entered note: :notes', - 'quote_reminder_subject' => 'Reminder: Quote :quote from :company', - 'quote_reminder_message' => 'Reminder for quote :number for :amount', - 'quote_reminder1' => 'First Quote Reminder', - 'before_valid_until_date' => 'Before the valid until date', - 'after_valid_until_date' => 'After the valid until date', - 'after_quote_date' => 'After the quote date', - 'remind_quote' => 'Remind Quote', - 'end_of_month' => 'End Of Month', - 'tax_currency_mismatch' => 'Tax currency is different from invoice currency', - 'edocument_import_already_exists' => 'The invoice has already been imported on :date', - 'before_valid_until' => 'Before the valid until', - 'after_valid_until' => 'After the valid until', - 'task_assigned_notification' => 'Task Assigned Notification', - 'task_assigned_notification_help' => 'Send an email when a task is assigned', - 'invoices_locked_end_of_month' => 'Invoices are locked at the end of the month', - 'referral_url' => 'Referral URL', - 'add_comment' => 'Add Comment', - 'added_comment' => 'Successfully saved comment', + 'end_of_month' => 'Fin de mes', + 'merge_e_invoice_to_pdf' => 'Fusionar factura electrónica y PDF', + 'task_assigned_subject' => 'Nueva asignación de tarea [Tarea :task] [ :date ]', + 'task_assigned_body' => 'Se le ha asignado la tarea :task

Descripción: :description

Cliente: :client', + 'activity_141' => 'El usuario: :user ingresó la nota: :notes', + 'quote_reminder_subject' => 'Recordatorio: Presupuesto :quote de :company', + 'quote_reminder_message' => 'Recordatorio de presupuesto :number por :amount', + 'quote_reminder1' => 'Recordatorio del primer presupuesto', + 'before_valid_until_date' => 'Antes del válido hasta la fecha', + 'after_valid_until_date' => 'Después de la fecha de validez hasta', + 'after_quote_date' => 'Después de la fecha del presupuesto', + 'remind_quote' => 'Recordatorio de presupuesto', + 'end_of_month' => 'Fin de mes', + 'tax_currency_mismatch' => 'La moneda del impuesto es diferente a la moneda de la factura', + 'edocument_import_already_exists' => 'La factura ya fué importada el :date', + 'before_valid_until' => 'Antes del válido hasta', + 'after_valid_until' => 'Después del válido hasta', + 'task_assigned_notification' => 'Notificación de tarea asignada', + 'task_assigned_notification_help' => 'Enviar un correo electrónico cuando se asigna una tarea', + 'invoices_locked_end_of_month' => 'Las facturas se bloquean al final del mes.', + 'referral_url' => 'URL de referencia', + 'add_comment' => 'Agregar comentario', + 'added_comment' => 'Comentario guardado correctamente', 'tickets' => 'Tickets', - 'assigned_group' => 'Successfully assigned group', - 'merge_to_pdf' => 'Merge to PDF', - 'latest_requires_php_version' => 'Note: the latest version requires PHP :version', - 'auto_expand_product_table_notes' => 'Automatically expand products table notes', - 'auto_expand_product_table_notes_help' => 'Automatically expands the notes section within the products table to display more lines.', + 'assigned_group' => 'Grupo asignado exitosamente', + 'merge_to_pdf' => 'Fusionar a PDF', + 'latest_requires_php_version' => 'Nota: la última versión requiere PHP :version', + 'auto_expand_product_table_notes' => 'Expandir automáticamente las notas de la tabla de productos', + 'auto_expand_product_table_notes_help' => 'Expande automáticamente la sección de notas dentro de la tabla de productos para mostrar más líneas.', + 'institution_number' => 'Número de institución', + 'transit_number' => 'Número de Tránsito', + 'personal' => 'Personal', + 'address_information' => 'Datos del Domicilio', + 'enter_the_information_for_the_bank_account' => 'Introduzca la información de la cuenta bancaria', + 'account_holder_information' => 'Información del titular de la cuenta', + 'enter_information_for_the_account_holder' => 'Introducir información del titular de la cuenta', + 'customer_type' => 'Tipo de cliente', + 'process_date' => 'Fecha de procesamiento', + 'forever_free' => 'Forever Free', ); return $lang; diff --git a/lang/fr_CA/texts.php b/lang/fr_CA/texts.php index 08e065a4a226..45fa3c61c7e5 100644 --- a/lang/fr_CA/texts.php +++ b/lang/fr_CA/texts.php @@ -5235,7 +5235,7 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'local_domain_help' => 'Domaine EHLO (facultatif)', 'port_help' => 'ex. 25,587,465', 'host_help' => 'ex. smtp.gmail.com', - 'always_show_required_fields' => 'Permet l\'affichage des champs requis d\'un formulaire', + 'always_show_required_fields' => 'Toujours afficher les champs requis d\'un formulaire', 'always_show_required_fields_help' => 'Affiche toujours les champs requis d\'un formulaire au paiement', 'advanced_cards' => 'Cartes avancées', 'activity_140' => 'État de compte envoyé à :client', @@ -5298,7 +5298,17 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette 'latest_requires_php_version' => 'Note: La dernière version requiert PHP :version', 'auto_expand_product_table_notes' => 'Développer automatiquement les notes du tableau de produits', 'auto_expand_product_table_notes_help' => '  -Développe automatiquement la section des notes dans le tableau de produits pour afficher plus de lignes.' +Développe automatiquement la section des notes dans le tableau de produits pour afficher plus de lignes.', + 'institution_number' => 'Numéro d\'institution', + 'transit_number' => 'Numéro de transit', + 'personal' => 'Personnel', + 'address_information' => 'Information d\'adresse', + 'enter_the_information_for_the_bank_account' => 'Entrez l\'information du compte de banque', + 'account_holder_information' => 'Information sur le détenteur du compte', + 'enter_information_for_the_account_holder' => 'Entrez l\'information du détenteur du compte', + 'customer_type' => 'Type de client', + 'process_date' => 'Date de traitement', + 'forever_free' => 'Gratuit pour toujours', ); return $lang; diff --git a/lang/hu/texts.php b/lang/hu/texts.php index b10b6b8f242e..62a6ab89f71f 100644 --- a/lang/hu/texts.php +++ b/lang/hu/texts.php @@ -3428,7 +3428,7 @@ adva :date', 'reminder2_sent' => 'Emlékeztető 2 elküldve', 'reminder3_sent' => 'Emlékeztető 3 elküldve', 'reminder_last_sent' => 'Utolsó emlékeztető elküldve', - 'pdf_page_info' => 'PDF oldal információ', + 'pdf_page_info' => 'PDF oldal :current / :total', 'emailed_credits' => 'E-mailezett jóváírások', 'view_in_stripe' => 'Megtekintés a Stripe-ban', 'rows_per_page' => 'Sorok száma oldalanként', @@ -5108,7 +5108,7 @@ adva :date', 'all_contacts' => 'Minden névjegy', 'insert_below' => 'Beszúrás alább', 'nordigen_handler_subtitle' => 'Bankszámla hitelesítés. Intézményének kiválasztása a kérelem kitöltéséhez a fiók hitelesítő adataival.', - 'nordigen_handler_error_heading_unknown' => 'Hiba történt', + 'nordigen_handler_error_heading_unknown' => 'An error has occurred', 'nordigen_handler_error_contents_unknown' => 'Ismeretlen hiba lépett fel! Ok:', 'nordigen_handler_error_heading_token_invalid' => 'Érvénytelen kód', 'nordigen_handler_error_contents_token_invalid' => 'A megadott token érvénytelen. Ha a probléma továbbra is fennáll, forduljon az ügyfélszolgálathoz.', @@ -5222,7 +5222,7 @@ adva :date', 'local_domain_help' => 'EHLO domain (optional)', 'port_help' => 'ie. 25,587,465', 'host_help' => 'ie. smtp.gmail.com', - 'always_show_required_fields' => 'Allows show required fields form', + 'always_show_required_fields' => 'Always show required fields form', 'always_show_required_fields_help' => 'Displays the required fields form always at checkout', 'advanced_cards' => 'Advanced Cards', 'activity_140' => 'Statement sent to :client', @@ -5285,6 +5285,16 @@ adva :date', 'latest_requires_php_version' => 'Note: the latest version requires PHP :version', 'auto_expand_product_table_notes' => 'Automatically expand products table notes', 'auto_expand_product_table_notes_help' => 'Automatically expands the notes section within the products table to display more lines.', + 'institution_number' => 'Institution Number', + 'transit_number' => 'Transit Number', + 'personal' => 'Personal', + 'address_information' => 'Address Information', + 'enter_the_information_for_the_bank_account' => 'Enter the Information for the Bank Account', + 'account_holder_information' => 'Account Holder Information', + 'enter_information_for_the_account_holder' => 'Enter Information for the Account Holder', + 'customer_type' => 'Customer Type', + 'process_date' => 'Process Date', + 'forever_free' => 'Forever Free', ); return $lang; diff --git a/lang/lo_LA/texts.php b/lang/lo_LA/texts.php index 3677f99b387c..77e3ebacc280 100644 --- a/lang/lo_LA/texts.php +++ b/lang/lo_LA/texts.php @@ -3444,7 +3444,7 @@ $lang = array( 'reminder2_sent' => 'ແຈ້ງເຕືອນ 2 ສົ່ງແລ້ວ', 'reminder3_sent' => 'ເຕືອນ 3 ສົ່ງແລ້ວ', 'reminder_last_sent' => 'ເຕືອນທີ່ສົ່ງຫຼ້າສຸດ', - 'pdf_page_info' => 'ໜ້າ :ປັດຈຸບັນຂອງ :ທັງໝົດ', + 'pdf_page_info' => 'ໜ້າ :current :total', 'emailed_credits' => 'ເຄຣດິດທີ່ສົ່ງອີເມວສຳເລັດແລ້ວ', 'view_in_stripe' => 'ເບິ່ງເປັນເສັ້ນດ່າງ', 'rows_per_page' => 'ແຖວຕໍ່ໜ້າ', @@ -5124,7 +5124,7 @@ $lang = array( 'all_contacts' => 'ຕິດຕໍ່ພົວພັນທັງໝົດ', 'insert_below' => 'ໃສ່ທາງລຸ່ມ', 'nordigen_handler_subtitle' => 'ການຢືນຢັນບັນຊີທະນາຄານ. ການເລືອກສະຖາບັນຂອງທ່ານເພື່ອເຮັດສໍາເລັດຄໍາຮ້ອງຂໍທີ່ມີຂໍ້ມູນປະຈໍາບັນຊີຂອງທ່ານ.', - 'nordigen_handler_error_heading_unknown' => 'ເກີດຄວາມຜິດພາດຂຶ້ນ', + 'nordigen_handler_error_heading_unknown' => 'An error has occurred', 'nordigen_handler_error_contents_unknown' => 'ມີຄວາມຜິດພາດທີ່ບໍ່ຮູ້ຈັກເກີດຂຶ້ນ! ເຫດ​ຜົນ:', 'nordigen_handler_error_heading_token_invalid' => 'ໂທເຄັນບໍ່ຖືກຕ້ອງ', 'nordigen_handler_error_contents_token_invalid' => 'ໂທເຄັນທີ່ໃຫ້ມາບໍ່ຖືກຕ້ອງ. ຕິດຕໍ່ຝ່າຍຊ່ວຍເຫຼືອເພື່ອຂໍຄວາມຊ່ວຍເຫຼືອ, ຖ້າບັນຫານີ້ຍັງຄົງຢູ່.', @@ -5238,7 +5238,7 @@ $lang = array( 'local_domain_help' => 'EHLO domain (optional)', 'port_help' => 'ie. 25,587,465', 'host_help' => 'ie. smtp.gmail.com', - 'always_show_required_fields' => 'ອະນຸຍາດໃຫ້ສະແດງແບບຟອມທີ່ຕ້ອງການ', + 'always_show_required_fields' => 'Always show required fields form', 'always_show_required_fields_help' => 'ສະແດງແບບຟອມຊ່ອງຂໍ້ມູນທີ່ຕ້ອງການຢູ່ສະເໝີໃນເວລາຈ່າຍເງິນ', 'advanced_cards' => 'ບັດຂັ້ນສູງ', 'activity_140' => 'Statement sent to :client', @@ -5301,6 +5301,16 @@ $lang = array( 'latest_requires_php_version' => 'Note: the latest version requires PHP :version', 'auto_expand_product_table_notes' => 'Automatically expand products table notes', 'auto_expand_product_table_notes_help' => 'Automatically expands the notes section within the products table to display more lines.', + 'institution_number' => 'Institution Number', + 'transit_number' => 'Transit Number', + 'personal' => 'Personal', + 'address_information' => 'Address Information', + 'enter_the_information_for_the_bank_account' => 'Enter the Information for the Bank Account', + 'account_holder_information' => 'Account Holder Information', + 'enter_information_for_the_account_holder' => 'Enter Information for the Account Holder', + 'customer_type' => 'Customer Type', + 'process_date' => 'Process Date', + 'forever_free' => 'Forever Free', ); return $lang; diff --git a/lang/pt_BR/texts.php b/lang/pt_BR/texts.php index 1422bd891ced..8dabd60b1596 100644 --- a/lang/pt_BR/texts.php +++ b/lang/pt_BR/texts.php @@ -1646,7 +1646,7 @@ Quando tiver as quantias, volte a esta página de formas de pagamento e clique " 'country_Turkey' => 'Turquia', 'country_Turkmenistan' => 'Turcomenistão', 'country_Turks and Caicos Islands' => 'Ilhas Turks e Caicos', - 'country_Tuvalu' => 'Tuvalu', + 'country_Tuvalu' => ' Tuvalu', 'country_Uganda' => 'Uganda', 'country_Ukraine' => 'Ucrânia', 'country_Macedonia, the former Yugoslav Republic of' => 'Macedônia, antiga República Iugoslava', @@ -3441,7 +3441,7 @@ Quando tiver as quantias, volte a esta página de formas de pagamento e clique " 'reminder2_sent' => 'Lembrete 2 Enviado', 'reminder3_sent' => 'Lembrete 3 Enviado', 'reminder_last_sent' => 'Último Lembrete Enviado', - 'pdf_page_info' => 'Página: atual de: total', + 'pdf_page_info' => 'Página: :current de: :total', 'emailed_credits' => 'Créditos enviados por e-mail com sucesso', 'view_in_stripe' => 'Ver em Listra', 'rows_per_page' => 'Linhas por Página', @@ -5121,7 +5121,7 @@ Quando tiver as quantias, volte a esta página de formas de pagamento e clique " 'all_contacts' => 'Todos os contatos', 'insert_below' => 'Inserir abaixo', 'nordigen_handler_subtitle' => 'Autenticação de conta bancária. Selecionando sua instituição para concluir a solicitação com as credenciais de sua conta.', - 'nordigen_handler_error_heading_unknown' => 'Ocorreu um erro', + 'nordigen_handler_error_heading_unknown' => 'An error has occurred', 'nordigen_handler_error_contents_unknown' => 'Um erro desconhecido ocorreu! Razão:', 'nordigen_handler_error_heading_token_invalid' => 'Token inválido', 'nordigen_handler_error_contents_token_invalid' => 'O token fornecido era inválido. Entre em contato com o suporte para obter ajuda, se o problema persistir.', @@ -5235,7 +5235,7 @@ Quando tiver as quantias, volte a esta página de formas de pagamento e clique " 'local_domain_help' => 'EHLO domain (optional)', 'port_help' => 'ie. 25,587,465', 'host_help' => 'ie. smtp.gmail.com', - 'always_show_required_fields' => 'Allows show required fields form', + 'always_show_required_fields' => 'Always show required fields form', 'always_show_required_fields_help' => 'Displays the required fields form always at checkout', 'advanced_cards' => 'Advanced Cards', 'activity_140' => 'Statement sent to :client', @@ -5298,6 +5298,16 @@ Quando tiver as quantias, volte a esta página de formas de pagamento e clique " 'latest_requires_php_version' => 'Note: the latest version requires PHP :version', 'auto_expand_product_table_notes' => 'Automatically expand products table notes', 'auto_expand_product_table_notes_help' => 'Automatically expands the notes section within the products table to display more lines.', + 'institution_number' => 'Institution Number', + 'transit_number' => 'Transit Number', + 'personal' => 'Personal', + 'address_information' => 'Address Information', + 'enter_the_information_for_the_bank_account' => 'Enter the Information for the Bank Account', + 'account_holder_information' => 'Account Holder Information', + 'enter_information_for_the_account_holder' => 'Enter Information for the Account Holder', + 'customer_type' => 'Customer Type', + 'process_date' => 'Process Date', + 'forever_free' => 'Forever Free', ); return $lang; diff --git a/lang/pt_PT/texts.php b/lang/pt_PT/texts.php index aa80e867fadc..768cae915cec 100644 --- a/lang/pt_PT/texts.php +++ b/lang/pt_PT/texts.php @@ -3443,7 +3443,7 @@ debitar da sua conta de acordo com essas instruções. Está elegível a um reem 'reminder2_sent' => 'Lembrete 2 Enviado', 'reminder3_sent' => 'Lembrete 3 Enviado', 'reminder_last_sent' => 'Último Lembrete Enviado', - 'pdf_page_info' => 'Página: atual de: total', + 'pdf_page_info' => 'Página: :current de: :total', 'emailed_credits' => 'Créditos enviados por e-mail com sucesso', 'view_in_stripe' => 'Ver em Formato Lista', 'rows_per_page' => 'Colunas por Página', diff --git a/tests/Feature/EInvoice/PeppolTest.php b/tests/Feature/EInvoice/PeppolTest.php index 547d688851a7..6c21951a714c 100644 --- a/tests/Feature/EInvoice/PeppolTest.php +++ b/tests/Feature/EInvoice/PeppolTest.php @@ -67,10 +67,8 @@ class PeppolTest extends TestCase $settings->country_id = '276'; $settings->currency_id = '3'; - $einvoice = new \InvoiceNinja\EInvoice\Models\Peppol\Invoice(); - $fib = new FinancialInstitutionBranch(); $fib->ID = "DEUTDEMMXXX"; //BIC $fib->Name = 'Deutsche Bank'; @@ -88,10 +86,13 @@ class PeppolTest extends TestCase $pm->PayeeFinancialAccount = $pfa; $einvoice->PaymentMeans[] = $pm; + $stub = new \stdClass; + $stub->Invoice = $einvoice; + $company = Company::factory()->create([ 'account_id' => $this->account->id, 'settings' => $settings, - 'e_invoice' => $einvoice, + 'e_invoice' => $stub, ]); $cu = CompanyUserFactory::create($this->user->id, $company->id, $this->account->id); @@ -149,11 +150,12 @@ class PeppolTest extends TestCase $this->assertEquals(119, $invoice->amount); - $peppol = new Peppol($invoice); $peppol->setInvoiceDefaults(); $peppol->run(); + nlog($peppol->toXml()); + $de_invoice = $peppol->getInvoice(); $this->assertNotNull($de_invoice); @@ -207,10 +209,14 @@ class PeppolTest extends TestCase $pm->PayeeFinancialAccount = $pfa; $einvoice->PaymentMeans[] = $pm; + + $stub = new \stdClass(); + $stub->Invoice = $einvoice; + $company = Company::factory()->create([ 'account_id' => $this->account->id, 'settings' => $settings, - 'e_invoice' => $einvoice, + 'e_invoice' => $stub, ]); $cu = CompanyUserFactory::create($this->user->id, $company->id, $this->account->id); diff --git a/tests/Feature/ExpenseApiTest.php b/tests/Feature/ExpenseApiTest.php index 81d7dafbf2ab..e6319f092847 100644 --- a/tests/Feature/ExpenseApiTest.php +++ b/tests/Feature/ExpenseApiTest.php @@ -314,21 +314,23 @@ class ExpenseApiTest extends TestCase public function testExpenseBulkCategorize() { + $eXX = Expense::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + ]); + $e = Expense::factory()->create([ 'company_id' => $this->company->id, 'user_id' => $this->user->id, ]); - $ec = ExpenseCategory::factory()->create([ 'company_id' => $this->company->id, 'user_id' => $this->user->id, 'name' => 'Test Category', ]); - nlog("expense category id = {$ec->hashed_id}"); - $data = [ 'category_id' => $ec->hashed_id, 'action' => 'bulk_categorize', @@ -341,11 +343,32 @@ class ExpenseApiTest extends TestCase ])->post('/api/v1/expenses/bulk', $data); $arr = $response->json(); - nlog($arr); - + $this->assertEquals($ec->hashed_id, $arr['data'][0]['category_id']); - } + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get("/api/v1/expenses"); + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertGreaterThan(1, count($arr['data'])); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get("/api/v1/expenses?categories={$ec->hashed_id}"); + + $response->assertStatus(200); + + $arr = $response->json(); + + $this->assertCount(1, $arr['data']); + + } public function testAddingExpense() { diff --git a/tests/Integration/Einvoice/Storecove/StorecoveTest.php b/tests/Integration/Einvoice/Storecove/StorecoveTest.php index 57882e1feb46..d227c6848254 100644 --- a/tests/Integration/Einvoice/Storecove/StorecoveTest.php +++ b/tests/Integration/Einvoice/Storecove/StorecoveTest.php @@ -28,7 +28,7 @@ class StorecoveTest extends TestCase use MockAccountData; use DatabaseTransactions; - private string $routing_id = ''; + private int $routing_id; protected function setUp(): void { @@ -352,7 +352,7 @@ $x = ' $sc = new \App\Services\EDocument\Gateway\Storecove\Storecove(); - $sc->sendDocument($x); + $sc->sendDocument($x, 290868); } */ @@ -398,10 +398,117 @@ $x = ' } + private function createESData() + { + $this->routing_id = 293098; + + $settings = CompanySettings::defaults(); + $settings->company_logo = 'https://pdf.invoicing.co/favicon-v2.png'; + $settings->website = 'www.invoiceninja.de'; + $settings->address1 = 'Calle Gran Vía, 28'; + $settings->address2 = 'Edificio Telefónica'; + $settings->city = 'Madrid'; + $settings->state = 'Madrid'; + $settings->postal_code = '28013'; + $settings->phone = '030 1234567'; + $settings->email = $this->faker->unique()->safeEmail(); + $settings->country_id = '724'; // Germany's ISO country code + $settings->vat_number = 'ESB16645678'; + $settings->id_number = 'HRB 12345'; + $settings->use_credits_payment = 'always'; + $settings->timezone_id = '1'; // CET (Central European Time) + $settings->entity_send_time = 0; + $settings->e_invoice_type = 'PEPPOL'; + $settings->currency_id = '3'; + $settings->classification = 'business'; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + ]); + + $this->user->companies()->attach($company->id, [ + 'account_id' => $this->account->id, + 'is_owner' => true, + 'is_admin' => 1, + 'is_locked' => 0, + 'permissions' => '', + 'notifications' => CompanySettings::notificationAdminDefaults(), + 'settings' => null, + ]); + + Client::unguard(); + + $c = + Client::create([ + 'company_id' => $company->id, + 'user_id' => $this->user->id, + 'name' => 'Empresa Ejemplo S.A.', + 'website' => 'https://www.empresa-ejemplo.es', + 'private_notes' => 'Estas son notas privadas para el cliente de prueba.', + 'balance' => 0, + 'paid_to_date' => 0, + 'vat_number' => 'ESB12345678', // Spanish VAT number with ES prefix + 'id_number' => 'B12345678', // Typical format for Spanish company registration numbers + 'custom_value1' => '2024-07-22 10:00:00', + 'custom_value2' => 'azul', // Spanish for blue + 'custom_value3' => 'palabraejemplo', // Spanish for sample word + 'custom_value4' => 'test@ejemplo.com', + 'address1' => 'Calle Ejemplo 123', + 'address2' => '2ª Planta, Oficina 45', + 'city' => 'Madrid', + 'state' => 'Madrid', + 'postal_code' => '28013', + 'country_id' => '724', // Spain + 'shipping_address1' => 'Calle Ejemplo 123', + 'shipping_address2' => '2ª Planta, Oficina 45', + 'shipping_city' => 'Madrid', + 'shipping_state' => 'Madrid', + 'shipping_postal_code' => '28013', + 'shipping_country_id' => '724', // Spain + 'settings' => ClientSettings::Defaults(), + 'client_hash' => \Illuminate\Support\Str::random(32), + 'routing_id' => '', + ]); + + $item = new InvoiceItem(); + $item->product_key = "Product Key"; + $item->notes = "Product Description"; + $item->cost = 10; + $item->quantity = 10; + $item->tax_rate1 = 21; + $item->tax_name1 = 'IVA'; + + $invoice = Invoice::factory()->create([ + 'company_id' => $company->id, + 'user_id' => $this->user->id, + 'client_id' => $c->id, + 'discount' => 0, + 'uses_inclusive_taxes' => false, + 'status_id' => 1, + 'tax_rate1' => 0, + 'tax_name1' => '', + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name2' => '', + 'tax_name3' => '', + 'line_items' => [$item], + 'number' => 'ES-'.rand(1000, 100000), + 'date' => now()->format('Y-m-d'), + 'due_date' => now()->addDays(14)->format('Y-m-d'), + ]); + + $invoice = $invoice->calc()->getInvoice(); + $invoice->service()->markSent()->save(); + + return $invoice; + + } + private function createDEData() { - $this->routing_id = '290868'; + $this->routing_id = 290868; $settings = CompanySettings::defaults(); $settings->company_logo = 'https://pdf.invoicing.co/favicon-v2.png'; @@ -495,7 +602,8 @@ $x = ' 'tax_name3' => '', 'line_items' => [$item], 'number' => 'DE-'.rand(1000, 100000), - 'date' => now()->format('Y-m-d') + 'date' => now()->format('Y-m-d'), + 'due_date' => now()->addDays(14)->format('Y-m-d'), ]); $invoice = $invoice->calc()->getInvoice(); @@ -506,6 +614,42 @@ $x = ' } + public function testEsRules() + { + + $invoice = $this->createESData(); + + $e_invoice = new \InvoiceNinja\EInvoice\Models\Peppol\Invoice(); + + $stub = json_decode('{"Invoice":{"Note":"Nooo","PaymentMeans":[{"ID":{"value":"afdasfasdfasdfas"},"PayeeFinancialAccount":{"Name":"PFA-NAME","ID":{"value":"DE89370400440532013000"},"AliasName":"PFA-Alias","AccountTypeCode":{"value":"CHECKING"},"AccountFormatCode":{"value":"IBAN"},"CurrencyCode":{"value":"EUR"},"FinancialInstitutionBranch":{"ID":{"value":"DEUTDEMMXXX"},"Name":"Deutsche Bank"}}}]}}'); + foreach($stub as $key => $value) { + $e_invoice->{$key} = $value; + } + + $invoice->e_invoice = $e_invoice; + $invoice->save(); + + $this->assertInstanceOf(Invoice::class, $invoice); + $this->assertInstanceof(\InvoiceNinja\EInvoice\Models\Peppol\Invoice::class, $e_invoice); + + $p = new Peppol($invoice); + + $p->run(); + $xml = $p->toXml(); + nlog($xml); + + $identifiers = [ + [ + 'scheme' => 'ES:VAT', + 'id' => 'ESB53625999' + ], + ]; + + $sc = new \App\Services\EDocument\Gateway\Storecove\Storecove(); + $sc->sendDocument($xml, $this->routing_id, $identifiers); + + } + public function testDeRules() { $invoice = $this->createDEData(); @@ -527,8 +671,16 @@ $x = ' $p->run(); $xml = $p->toXml(); nlog($xml); - $sc = new \App\Services\EDocument\Gateway\Storecove\Storecove(); - $sc->sendDocument($xml); + + $identifiers = [ + [ + 'scheme' => 'DE:VAT', + 'id' => 'DE010101010' + ] + ]; + + $sc = new \App\Services\EDocument\Gateway\Storecove\Storecove(); + $sc->sendDocument($xml, $this->routing_id, $identifiers); }