mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-04 03:17:35 -05:00 
			
		
		
		
	Merge pull request #8525 from turbo124/v5-develop
Improve auto bill text in client portal
This commit is contained in:
		
						commit
						21468a8576
					
				@ -129,11 +129,13 @@ class Rule extends BaseRule implements RuleInterface
 | 
			
		||||
     */
 | 
			
		||||
    public function taxShipping($item): self
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if($this->tax_data?->txbFreight == 'Y') {
 | 
			
		||||
            $this->default($item);
 | 
			
		||||
            return $this->default($item);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->tax_rate1 = 0;
 | 
			
		||||
        $this->tax_name1 = '';
 | 
			
		||||
        
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,8 @@ class BaseExport
 | 
			
		||||
 | 
			
		||||
    public string $client_description = 'All Clients';
 | 
			
		||||
 | 
			
		||||
    public array $forced_keys = [];
 | 
			
		||||
 | 
			
		||||
    protected function filterByClients($query)
 | 
			
		||||
    {
 | 
			
		||||
        if (isset($this->input['client_id']) && $this->input['client_id'] != 'all') {
 | 
			
		||||
@ -170,7 +172,7 @@ class BaseExport
 | 
			
		||||
    {
 | 
			
		||||
        $header = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($this->input['report_keys'] as $value) {
 | 
			
		||||
        foreach (array_merge($this->input['report_keys'], $this->forced_keys) as $value) {
 | 
			
		||||
            $key = array_search($value, $this->entity_keys);
 | 
			
		||||
 | 
			
		||||
            $key = str_replace('item.', '', $key);
 | 
			
		||||
 | 
			
		||||
@ -81,6 +81,10 @@ class ClientExport extends BaseExport
 | 
			
		||||
        'client.industry',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    public array $forced_keys = [
 | 
			
		||||
        'status',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    public function __construct(Company $company, array $input)
 | 
			
		||||
    {
 | 
			
		||||
        $this->company = $company;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										65
									
								
								app/Jobs/Client/CheckVat.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								app/Jobs/Client/CheckVat.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,65 @@
 | 
			
		||||
<?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\Jobs\Client;
 | 
			
		||||
 | 
			
		||||
use App\Models\Client;
 | 
			
		||||
use App\Models\Company;
 | 
			
		||||
use App\Libraries\MultiDB;
 | 
			
		||||
use Illuminate\Bus\Queueable;
 | 
			
		||||
use App\DataProviders\USStates;
 | 
			
		||||
use App\Utils\Traits\MakesHash;
 | 
			
		||||
use App\Services\Tax\TaxService;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
			
		||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
 | 
			
		||||
 | 
			
		||||
class CheckVat implements ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
    use Dispatchable;
 | 
			
		||||
    use InteractsWithQueue;
 | 
			
		||||
    use Queueable;
 | 
			
		||||
    use SerializesModels;
 | 
			
		||||
    use MakesHash;
 | 
			
		||||
 | 
			
		||||
    public $tries = 1;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new job instance.
 | 
			
		||||
     *
 | 
			
		||||
     * @param Client $client
 | 
			
		||||
     * @param Company $company
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct(public Client $client, protected Company $company)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Execute the job.
 | 
			
		||||
     *
 | 
			
		||||
     */
 | 
			
		||||
    public function handle()
 | 
			
		||||
    {
 | 
			
		||||
        MultiDB::setDb($this->company->db);
 | 
			
		||||
 | 
			
		||||
        $tax_service = new TaxService($this->client);
 | 
			
		||||
        $tax_service->validateVat();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function middleware()
 | 
			
		||||
    {
 | 
			
		||||
        return [new WithoutOverlapping($this->client->id)];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -75,7 +75,7 @@ class ReminderJob implements ShouldQueue
 | 
			
		||||
                         $this->sendReminderForInvoice($invoice);
 | 
			
		||||
                     }
 | 
			
		||||
 | 
			
		||||
                     sleep(2);
 | 
			
		||||
                     sleep(1);
 | 
			
		||||
                 });
 | 
			
		||||
        } else {
 | 
			
		||||
            //multiDB environment, need to
 | 
			
		||||
@ -105,7 +105,7 @@ class ReminderJob implements ShouldQueue
 | 
			
		||||
                             $this->sendReminderForInvoice($invoice);
 | 
			
		||||
                         }
 | 
			
		||||
 | 
			
		||||
                         sleep(2);
 | 
			
		||||
                         sleep(1);
 | 
			
		||||
                     });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -11,8 +11,10 @@
 | 
			
		||||
 | 
			
		||||
namespace App\Observers;
 | 
			
		||||
 | 
			
		||||
use App\Utils\Ninja;
 | 
			
		||||
use App\Models\Client;
 | 
			
		||||
use App\Models\Webhook;
 | 
			
		||||
use App\Jobs\Client\CheckVat;
 | 
			
		||||
use App\Jobs\Util\WebhookHandler;
 | 
			
		||||
use App\Jobs\Client\UpdateTaxData;
 | 
			
		||||
 | 
			
		||||
@ -20,6 +22,36 @@ class ClientObserver
 | 
			
		||||
{
 | 
			
		||||
    public $afterCommit = true;
 | 
			
		||||
 | 
			
		||||
    private $eu_country_codes = [
 | 
			
		||||
        'AT' => '40', 
 | 
			
		||||
        'BE' => '56', 
 | 
			
		||||
        'BG' => '100', 
 | 
			
		||||
        'CY' => '196', 
 | 
			
		||||
        'CZ' => '203', 
 | 
			
		||||
        'DE' => '276', 
 | 
			
		||||
        'DK' => '208', 
 | 
			
		||||
        'EE' => '233', 
 | 
			
		||||
        'ES' => '724', 
 | 
			
		||||
        'FI' => '246', 
 | 
			
		||||
        'FR' => '250', 
 | 
			
		||||
        'GR' => '300', 
 | 
			
		||||
        'HR' => '191', 
 | 
			
		||||
        'HU' => '348', 
 | 
			
		||||
        'IE' => '372', 
 | 
			
		||||
        'IT' => '380', 
 | 
			
		||||
        'LT' => '440', 
 | 
			
		||||
        'LU' => '442', 
 | 
			
		||||
        'LV' => '428', 
 | 
			
		||||
        'MT' => '470', 
 | 
			
		||||
        'NL' => '528', 
 | 
			
		||||
        'PL' => '616', 
 | 
			
		||||
        'PT' => '620', 
 | 
			
		||||
        'RO' => '642', 
 | 
			
		||||
        'SE' => '752', 
 | 
			
		||||
        'SI' => '705', 
 | 
			
		||||
        'SK' => '703', 
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handle the client "created" event.
 | 
			
		||||
     *
 | 
			
		||||
@ -33,6 +65,10 @@ class ClientObserver
 | 
			
		||||
            UpdateTaxData::dispatch($client, $client->company);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(in_array($client->country_id, $this->eu_country_codes) && $client->company->calculate_taxes) {
 | 
			
		||||
            CheckVat::dispatch($client, $client->company);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $subscriptions = Webhook::where('company_id', $client->company_id)
 | 
			
		||||
                                    ->where('event_id', Webhook::EVENT_CREATE_CLIENT)
 | 
			
		||||
                                    ->exists();
 | 
			
		||||
@ -50,11 +86,17 @@ class ClientObserver
 | 
			
		||||
     */
 | 
			
		||||
    public function updated(Client $client)
 | 
			
		||||
    {
 | 
			
		||||
        if($client->getOriginal('postal_code') != $client->postal_code && $client->country_id == 840 && $client->company->calculate_taxes)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        /** Monitor postal code changes for US based clients for tax calculations */
 | 
			
		||||
        if(Ninja::isHosted() && $client->getOriginal('postal_code') != $client->postal_code && $client->country_id == 840 && $client->company->calculate_taxes) {
 | 
			
		||||
            UpdateTaxData::dispatch($client, $client->company);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /** Monitor vat numbers for EU based clients for tax calculations */
 | 
			
		||||
        if($client->getOriginal('vat_number') != $client->vat_number && in_array($client->country_id, $this->eu_country_codes) && $client->company->calculate_taxes) {
 | 
			
		||||
            CheckVat::dispatch($client, $client->company);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $event = Webhook::EVENT_UPDATE_CLIENT;
 | 
			
		||||
 | 
			
		||||
        if ($client->getOriginal('deleted_at') && !$client->deleted_at) {
 | 
			
		||||
 | 
			
		||||
@ -117,9 +117,12 @@ class ClientRepository extends BaseRepository
 | 
			
		||||
     */
 | 
			
		||||
    public function create($client): ?Client
 | 
			
		||||
    {
 | 
			
		||||
        /** @var \App\Models\User $user */
 | 
			
		||||
        $user = auth()->user();
 | 
			
		||||
 | 
			
		||||
        return $this->save(
 | 
			
		||||
            $client,
 | 
			
		||||
            ClientFactory::create(auth()->user()->company()->id, auth()->user()->id)
 | 
			
		||||
            ClientFactory::create($user->company()->id, $user->id)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -31,6 +31,8 @@ class ProcessBankRules extends AbstractService
 | 
			
		||||
 | 
			
		||||
    protected $categories;
 | 
			
		||||
 | 
			
		||||
    protected $invoices;
 | 
			
		||||
    
 | 
			
		||||
    public function __construct(public BankTransaction $bank_transaction)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,7 @@ class EmailStatementService
 | 
			
		||||
 | 
			
		||||
        //Email only the selected clients
 | 
			
		||||
        if (count($this->scheduler->parameters['clients']) >= 1) {
 | 
			
		||||
            $query->whereIn('id', $this->transformKeys($this->scheduler->parameters['clients']));
 | 
			
		||||
            $query->whereIn('id', $this->transformKeys($this->scheduler->parameters['clients']))->where('balance', '>', 0);
 | 
			
		||||
        }
 | 
			
		||||
     
 | 
			
		||||
        $query->cursor()
 | 
			
		||||
 | 
			
		||||
@ -22,14 +22,34 @@ class TaxService
 | 
			
		||||
 | 
			
		||||
    public function validateVat(): self
 | 
			
		||||
    {
 | 
			
		||||
        if(!extension_loaded('soap')) {
 | 
			
		||||
            nlog("Install the PHP SOAP extension if you wish to check VAT Numbers. See https://www.php.net/manual/en/soap.installation.php for more information on installing the PHP");
 | 
			
		||||
            return $this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $client_country_code = $this->client->shipping_country ? $this->client->shipping_country->iso_3166_2 : $this->client->country->iso_3166_2;
 | 
			
		||||
 | 
			
		||||
        $vat_check = (new VatNumberCheck($this->client->vat_number, $client_country_code))->run();
 | 
			
		||||
 | 
			
		||||
        $this->client->has_valid_vat_number = $vat_check->isValid();
 | 
			
		||||
        nlog($vat_check);
 | 
			
		||||
 | 
			
		||||
        if($vat_check->isValid()) {
 | 
			
		||||
 | 
			
		||||
            $this->client->has_valid_vat_number = true;
 | 
			
		||||
 | 
			
		||||
                if(!$this->client->name && strlen($vat_check->getName()) > 2) {
 | 
			
		||||
                    $this->client->name = $vat_check->getName();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if(empty($this->client->private_notes) && strlen($vat_check->getAddress()) > 2) {
 | 
			
		||||
                    $this->client->private_notes = $vat_check->getAddress();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            $this->client->saveQuietly();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function initTaxProvider()
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@ class VatNumberCheck
 | 
			
		||||
{
 | 
			
		||||
    private array $response = [];
 | 
			
		||||
 | 
			
		||||
    public function __construct(protected string $vat_number, protected string $country_code)
 | 
			
		||||
    public function __construct(protected ?string $vat_number, protected string $country_code)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ class VatNumberCheck
 | 
			
		||||
            $client = new \SoapClient($wsdl);
 | 
			
		||||
            $params = [
 | 
			
		||||
                'countryCode' => $this->country_code,
 | 
			
		||||
                'vatNumber' => $this->vat_number
 | 
			
		||||
                'vatNumber' => $this->vat_number ?? ''
 | 
			
		||||
            ];
 | 
			
		||||
            $response = $client->checkVat($params);
 | 
			
		||||
 | 
			
		||||
@ -63,4 +63,14 @@ class VatNumberCheck
 | 
			
		||||
    {
 | 
			
		||||
        return $this->response['valid'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getName()
 | 
			
		||||
    {
 | 
			
		||||
        return isset($this->response['name']) ? $this->response['name'] : '';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getAddress()
 | 
			
		||||
    {
 | 
			
		||||
        return isset($this->response['address']) ? $this->response['address'] : '';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5094,6 +5094,7 @@ $LANG = array(
 | 
			
		||||
    'order_id' => 'Order',
 | 
			
		||||
    'total_invoices_outstanding' => 'Total Invoices Outstanding',
 | 
			
		||||
    'recent_activity' => 'Recent Activity',
 | 
			
		||||
    'enable_auto_bill' => 'Enable auto billing',
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,6 @@
 | 
			
		||||
           wire:change="updateAutoBilling" {{ $invoice->auto_bill_enabled ? 'checked' : '' }}>
 | 
			
		||||
 | 
			
		||||
    <span class="text-sm leading-5 font-medium text-gray-900">
 | 
			
		||||
        {{ $invoice->auto_bill_enabled || $invoice->auto_bill === 'optout' ? ctrans('texts.auto_bill_enabled') : ctrans('texts.auto_bill_disabled') }}
 | 
			
		||||
        {{ ctrans('texts.enable_auto_bill') }}
 | 
			
		||||
    </span>
 | 
			
		||||
</label>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user