mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-04 00:17:34 -05:00 
			
		
		
		
	Merge pull request #9565 from turbo124/v5-develop
Updates for currencies
This commit is contained in:
		
						commit
						326f6e1f73
					
				
							
								
								
									
										9
									
								
								.github/workflows/phpunit.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.github/workflows/phpunit.yml
									
									
									
									
										vendored
									
									
								
							@ -13,8 +13,8 @@ jobs:
 | 
			
		||||
    runs-on: ${{ matrix.operating-system }}
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        operating-system: ['ubuntu-20.04', 'ubuntu-22.04']
 | 
			
		||||
        php-versions: ['8.1','8.2']
 | 
			
		||||
        operating-system: ['ubuntu-22.04','ubuntu-24.04']
 | 
			
		||||
        php-versions: ['8.2']
 | 
			
		||||
        phpunit-versions: ['latest']
 | 
			
		||||
        ci_node_total: [ 8 ]
 | 
			
		||||
        ci_node_index: [ 0, 1, 2, 3, 4, 5, 6, 7]
 | 
			
		||||
@ -95,14 +95,15 @@ jobs:
 | 
			
		||||
    - name: Get Composer Cache Directory
 | 
			
		||||
      id: composer-cache
 | 
			
		||||
      run: |
 | 
			
		||||
        echo "::set-output name=dir::$(composer config cache-files-dir)"
 | 
			
		||||
    - uses: actions/cache@v3
 | 
			
		||||
        echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
 | 
			
		||||
    - uses: actions/cache@v4
 | 
			
		||||
      with:
 | 
			
		||||
        path: ${{ steps.composer-cache.outputs.dir }}
 | 
			
		||||
        key: ${{ runner.os }}-${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }}
 | 
			
		||||
        restore-keys: |
 | 
			
		||||
          ${{ runner.os }}-${{ matrix.php }}-composer-
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    - name: Install composer dependencies
 | 
			
		||||
      run: |
 | 
			
		||||
        composer config -g github-oauth.github.com ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
@ -1,69 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
 *
 | 
			
		||||
 * @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
 *
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Console\Commands;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Console\Command;
 | 
			
		||||
use Illuminate\Support\Facades\Storage;
 | 
			
		||||
use App\DataProviders\FatturaPADataProvider;
 | 
			
		||||
 | 
			
		||||
class EDocLint extends Command
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * The name and signature of the console command.
 | 
			
		||||
     *
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    protected $signature = 'ninja:edoclint';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The console command description.
 | 
			
		||||
     *
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    protected $description = 'Builds json files component data maps';
 | 
			
		||||
 | 
			
		||||
    private array $classes = [
 | 
			
		||||
        FatturaPADataProvider::class,
 | 
			
		||||
    ];
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new command instance.
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct()
 | 
			
		||||
    {
 | 
			
		||||
        parent::__construct();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Execute the console command.
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function handle()
 | 
			
		||||
    {
 | 
			
		||||
        foreach($this->classes as $class)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            $provider = new $class();
 | 
			
		||||
 | 
			
		||||
            foreach($provider as $key => $value) {
 | 
			
		||||
 | 
			
		||||
                $json = json_encode($provider->{$key}, JSON_PRETTY_PRINT);
 | 
			
		||||
                Storage::disk('local')->put($key.'.json', $json);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								app/DataProviders/FACT1.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								app/DataProviders/FACT1.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
* Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
*
 | 
			
		||||
* @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
*
 | 
			
		||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
*
 | 
			
		||||
* @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace App\DataProviders;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class FACT1.
 | 
			
		||||
 */
 | 
			
		||||
class FACT1
 | 
			
		||||
{
 | 
			
		||||
    public function build()
 | 
			
		||||
    {
 | 
			
		||||
        $i = new \InvoiceNinja\EInvoice\Models\FACT1\Invoice();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,124 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
* Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
*
 | 
			
		||||
* @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
*
 | 
			
		||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
*
 | 
			
		||||
* @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
namespace App\DataProviders;
 | 
			
		||||
 | 
			
		||||
class FatturaPADataProvider
 | 
			
		||||
{
 | 
			
		||||
    public array $regime_fiscale = [
 | 
			
		||||
        "RF01" => "Regime ordinario",
 | 
			
		||||
        "RF02" => "Regime dei contribuenti minimi (art. 1,c.96-117, L. 244/2007)",
 | 
			
		||||
        "RF04" => "Agricoltura e attività connesse e pesca (artt. 34 e 34-bis, D.P.R. 633/1972)",
 | 
			
		||||
        "RF05" => "Vendita sali e tabacchi (art. 74, c.1, D.P.R. 633/1972)",
 | 
			
		||||
        "RF06" => "Commercio dei fiammiferi (art. 74, c.1, D.P.R. 633/1972)",
 | 
			
		||||
        "RF07" => "Editoria (art. 74, c.1, D.P.R. 633/1972)",
 | 
			
		||||
        "RF08" => "Gestione di servizi di telefonia pubblica (art. 74, c.1, D.P.R. 633/1972)" ,
 | 
			
		||||
        "RF09" => "Rivendita di documenti di trasporto pubblico e di sosta (art. 74, c.1, D.P.R. 633/1972)" ,
 | 
			
		||||
        "RF10" => "Intrattenimenti, giochi e altre attività di cui alla tariffa allegata al D.P.R. 640/72 (art. 74, c.6, D.P.R. 633/1972)" ,
 | 
			
		||||
        "RF11" => "Agenzie di viaggi e turismo (art. 74-ter, D.P.R. 633/1972)" ,
 | 
			
		||||
        "RF12" => "Agriturismo (art. 5, c.2, L. 413/1991)" ,
 | 
			
		||||
        "RF13" => "Vendite a domicilio (art. 25-bis, c.6, D.P.R. 600/1973)" ,
 | 
			
		||||
        "RF14" => "Rivendita di beni usati, di oggetti d’arte, d’antiquariato o da collezione (art. 36, D.L. 41/1995)" ,
 | 
			
		||||
        "RF15" => "Agenzie di vendite all’asta di oggetti d’arte, antiquariato o da collezione (art. 40-bis, D.L. 41/1995)" ,
 | 
			
		||||
        "RF16" => "IVA per cassa P.A. (art. 6, c.5, D.P.R. 633/1972)" ,
 | 
			
		||||
        "RF17" => "IVA per cassa (art. 32-bis, D.L. 83/2012)" ,
 | 
			
		||||
        "RF19" => "Regime forfettario" ,
 | 
			
		||||
        "RF18" => "Altro"
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    public array $tipo_documento = [
 | 
			
		||||
        'TD01' => 'Fattura',
 | 
			
		||||
        'TD02' => 'Acconto/Anticipo su fattura',
 | 
			
		||||
        'TD03' => 'Acconto/Anticipo su parcella',
 | 
			
		||||
        'TD04' => 'Nota di Credito',
 | 
			
		||||
        'TD05' => 'Nota di Debito',
 | 
			
		||||
        'TD06' => 'Parcella',
 | 
			
		||||
        'TD16' => 'Integrazione fattura reverse charge interno',
 | 
			
		||||
        'TD17' => 'Integrazione/autofattura per acquisto servizi dall’estero',
 | 
			
		||||
        'TD18' => 'Integrazione per acquisto di beni intracomunitari',
 | 
			
		||||
        'TD19' => 'Integrazione/autofattura per acquisto di beni ex art.17 c.2 DPR 633/72',
 | 
			
		||||
        'TD20' => 'Autofattura per regolarizzazione e integrazione delle fatture',
 | 
			
		||||
        'TD21' => 'Autofattura per splafonamento',
 | 
			
		||||
        'TD22' => 'Estrazione beni da Deposito IVA',
 | 
			
		||||
        'TD23' => 'Estrazione beni da Deposito IVA con versamento dell’IVA',
 | 
			
		||||
        'TD24' => 'Fattura differita di cui all’art.21, comma 4, lett. a)',
 | 
			
		||||
        'TD25' => 'Fattura differita di cui all’art.21, comma 4, terzo periodo lett. b)',
 | 
			
		||||
        'TD26' => 'Cessione di beni ammortizzabili e per passaggi interni ',
 | 
			
		||||
        'TD27' => 'Fattura per autoconsumo o per cessioni gratuite senza rivalsa',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    public array $esigibilita_iva = [
 | 
			
		||||
        'I' => 'IVA ad esigibilità immediata',
 | 
			
		||||
        'D' => 'IVA ad esigibilità differita',
 | 
			
		||||
        'S' => 'Scissione dei pagamenti',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    public array $modalita_pagamento = [
 | 
			
		||||
        'MP01' => 'contanti', //cash
 | 
			
		||||
        'MP02' => 'assegno', //check
 | 
			
		||||
        'MP03' => 'assegno circolare', //cashier's check
 | 
			
		||||
        'MP04' => 'contanti presso Tesoreria', //cash at treasury
 | 
			
		||||
        'MP05' => 'bonifico', //bank transfer
 | 
			
		||||
        'MP06' => 'vaglia cambiario', //bill of exchange
 | 
			
		||||
        'MP07' => 'bollettino bancario', //bank bulletin
 | 
			
		||||
        'MP08' => 'carta di pagamento', //payment card
 | 
			
		||||
        'MP09' => 'RID', //RID
 | 
			
		||||
        'MP10' => 'RID utenze', //RID utilities
 | 
			
		||||
        'MP11' => 'RID veloce', //fast RID
 | 
			
		||||
        'MP12' => 'Riba', //Riba
 | 
			
		||||
        'MP13' => 'MAV //MAV',
 | 
			
		||||
        'MP14' => 'quietanza erario stato', //state treasury receipt
 | 
			
		||||
        'MP15' => 'giroconto su conti di contabilità speciale', //transfer to special accounting accounts
 | 
			
		||||
        'MP16' => 'domiciliazione bancaria', //bank domiciliation
 | 
			
		||||
        'MP17' => 'domiciliazione postale', //postal domiciliation
 | 
			
		||||
        'MP18' => 'bollettino di c/c postale', //postal giro account
 | 
			
		||||
        'MP19' => 'SEPA Direct Debit', //SEPA Direct Debit
 | 
			
		||||
        'MP20' => 'SEPA Direct Debit CORE', //SEPA Direct Debit CORE
 | 
			
		||||
        'MP21' => 'SEPA Direct Debit B2B', //SEPA Direct Debit B2B
 | 
			
		||||
        'MP22' => 'Trattenuta su somme già riscosse', //Withholding on sums already collected
 | 
			
		||||
        'MP23' => 'PagoPA', //PagoPA
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    public array $esigibilita_pagamento = [
 | 
			
		||||
        'TP01' => 'Pagamento a rate',
 | 
			
		||||
        'TP02' => 'Pagamento completo',
 | 
			
		||||
        'TP03' => 'Anticipo',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    public function __construct()
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public function getRegimeFiscale(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->regime_fiscale;
 | 
			
		||||
    }
 | 
			
		||||
    public function getTipoDocumento(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->tipo_documento;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public function getEsigibilitaIva(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->esigibilita_iva;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getModalitaPagamento(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->modalita_pagamento;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getEsigibilitaPagamento(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->esigibilita_pagamento;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -125,8 +125,10 @@ class ClientExport extends BaseExport
 | 
			
		||||
 | 
			
		||||
        $query = Client::query()->with('contacts')
 | 
			
		||||
                                ->withTrashed()
 | 
			
		||||
                                ->where('company_id', $this->company->id)
 | 
			
		||||
                                ->where('is_deleted', $this->input['include_deleted'] ?? false);
 | 
			
		||||
                                ->where('company_id', $this->company->id);
 | 
			
		||||
 | 
			
		||||
        if(!$this->input['include_deleted'] ?? false)
 | 
			
		||||
            $query->where('is_deleted', 0);
 | 
			
		||||
 | 
			
		||||
        $query = $this->addDateRange($query);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -82,8 +82,12 @@ class ExpenseExport extends BaseExport
 | 
			
		||||
        $query = Expense::query()
 | 
			
		||||
                        ->with('client')
 | 
			
		||||
                        ->withTrashed()
 | 
			
		||||
                        ->where('company_id', $this->company->id)
 | 
			
		||||
                        ->where('is_deleted', $this->input['include_deleted'] ?? false);
 | 
			
		||||
                        ->where('company_id', $this->company->id);
 | 
			
		||||
        
 | 
			
		||||
                        
 | 
			
		||||
        if(!$this->input['include_deleted'] ?? false){
 | 
			
		||||
            $query->where('is_deleted', 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $query = $this->addDateRange($query);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -60,8 +60,12 @@ class InvoiceExport extends BaseExport
 | 
			
		||||
                        ->whereHas('client', function ($q){
 | 
			
		||||
                            $q->where('is_deleted', false);
 | 
			
		||||
                        })
 | 
			
		||||
                        ->where('company_id', $this->company->id)
 | 
			
		||||
                        ->where('is_deleted', $this->input['include_deleted'] ?? false);
 | 
			
		||||
                        ->where('company_id', $this->company->id);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if(!$this->input['include_deleted'] ?? false){
 | 
			
		||||
            $query->where('is_deleted', 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $query = $this->addDateRange($query);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -73,8 +73,11 @@ class InvoiceItemExport extends BaseExport
 | 
			
		||||
                        ->whereHas('client', function ($q){
 | 
			
		||||
                            $q->where('is_deleted', false);
 | 
			
		||||
                        })
 | 
			
		||||
                        ->where('company_id', $this->company->id)
 | 
			
		||||
                        ->where('is_deleted', $this->input['include_deleted'] ?? false);
 | 
			
		||||
                        ->where('company_id', $this->company->id);
 | 
			
		||||
                        
 | 
			
		||||
        if(!$this->input['include_deleted'] ?? false){
 | 
			
		||||
            $query->where('is_deleted', 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $query = $this->addDateRange($query);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -73,8 +73,12 @@ class ProductExport extends BaseExport
 | 
			
		||||
 | 
			
		||||
        $query = Product::query()
 | 
			
		||||
                        ->withTrashed()
 | 
			
		||||
                        ->where('company_id', $this->company->id)
 | 
			
		||||
                        ->where('is_deleted', 0);
 | 
			
		||||
                        ->where('company_id', $this->company->id);
 | 
			
		||||
                        
 | 
			
		||||
                        
 | 
			
		||||
        if(!$this->input['include_deleted'] ?? false){
 | 
			
		||||
            $query->where('is_deleted', 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $query = $this->addDateRange($query);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -61,8 +61,11 @@ class PurchaseOrderExport extends BaseExport
 | 
			
		||||
                        ->whereHas('vendor', function ($q){
 | 
			
		||||
                            $q->where('is_deleted', false);
 | 
			
		||||
                        })
 | 
			
		||||
                        ->where('company_id', $this->company->id)
 | 
			
		||||
                        ->where('is_deleted', $this->input['include_deleted'] ?? false);
 | 
			
		||||
                        ->where('company_id', $this->company->id);
 | 
			
		||||
                        
 | 
			
		||||
        if(!$this->input['include_deleted'] ?? false){
 | 
			
		||||
            $query->where('is_deleted', 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $query = $this->addDateRange($query);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -65,8 +65,11 @@ class PurchaseOrderItemExport extends BaseExport
 | 
			
		||||
                        ->whereHas('vendor', function ($q){
 | 
			
		||||
                            $q->where('is_deleted', false);
 | 
			
		||||
                        })
 | 
			
		||||
                        ->with('vendor')->where('company_id', $this->company->id)
 | 
			
		||||
                        ->where('is_deleted', $this->input['include_deleted'] ?? false);
 | 
			
		||||
                        ->with('vendor')->where('company_id', $this->company->id);
 | 
			
		||||
                        
 | 
			
		||||
        if(!$this->input['include_deleted'] ?? false){
 | 
			
		||||
            $query->where('is_deleted', 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $query = $this->addDateRange($query);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -67,8 +67,11 @@ class QuoteExport extends BaseExport
 | 
			
		||||
                        ->whereHas('client', function ($q){
 | 
			
		||||
                            $q->where('is_deleted', false);
 | 
			
		||||
                        })
 | 
			
		||||
                        ->where('company_id', $this->company->id)
 | 
			
		||||
                        ->where('is_deleted', $this->input['include_deleted'] ?? false);
 | 
			
		||||
                        ->where('company_id', $this->company->id);
 | 
			
		||||
                        
 | 
			
		||||
        if(!$this->input['include_deleted'] ?? false){
 | 
			
		||||
            $query->where('is_deleted', 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $query = $this->addDateRange($query);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -68,8 +68,11 @@ class QuoteItemExport extends BaseExport
 | 
			
		||||
                            ->whereHas('client', function ($q){
 | 
			
		||||
                                $q->where('is_deleted', false);
 | 
			
		||||
                            })
 | 
			
		||||
                            ->with('client')->where('company_id', $this->company->id)
 | 
			
		||||
                            ->where('is_deleted', $this->input['include_deleted'] ?? false);
 | 
			
		||||
                            ->with('client')->where('company_id', $this->company->id);
 | 
			
		||||
                            
 | 
			
		||||
        if(!$this->input['include_deleted'] ?? false){
 | 
			
		||||
            $query->where('is_deleted', 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $query = $this->addDateRange($query);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -59,8 +59,11 @@ class RecurringInvoiceExport extends BaseExport
 | 
			
		||||
                        ->whereHas('client', function ($q){
 | 
			
		||||
                            $q->where('is_deleted', false);
 | 
			
		||||
                        })
 | 
			
		||||
                        ->where('company_id', $this->company->id)
 | 
			
		||||
                        ->where('is_deleted', $this->input['include_deleted'] ?? false);
 | 
			
		||||
                        ->where('company_id', $this->company->id);
 | 
			
		||||
                        
 | 
			
		||||
        if(!$this->input['include_deleted'] ?? false){
 | 
			
		||||
            $query->where('is_deleted', 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $query = $this->addDateRange($query);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -68,8 +68,11 @@ class TaskExport extends BaseExport
 | 
			
		||||
 | 
			
		||||
        $query = Task::query()
 | 
			
		||||
                        ->withTrashed()
 | 
			
		||||
                        ->where('company_id', $this->company->id)
 | 
			
		||||
                        ->where('is_deleted', $this->input['include_deleted'] ?? false);
 | 
			
		||||
                        ->where('company_id', $this->company->id);
 | 
			
		||||
                        
 | 
			
		||||
        if(!$this->input['include_deleted'] ?? false){
 | 
			
		||||
            $query->where('is_deleted', 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $query = $this->addDateRange($query);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
@ -62,8 +62,11 @@ class VendorExport extends BaseExport
 | 
			
		||||
 | 
			
		||||
        $query = Vendor::query()->with('contacts')
 | 
			
		||||
                        ->withTrashed()
 | 
			
		||||
                        ->where('company_id', $this->company->id)
 | 
			
		||||
                        ->where('is_deleted', $this->input['include_deleted'] ?? false);
 | 
			
		||||
                        ->where('company_id', $this->company->id);
 | 
			
		||||
                        
 | 
			
		||||
        if(!$this->input['include_deleted'] ?? false){
 | 
			
		||||
            $query->where('is_deleted', 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $query = $this->addDateRange($query);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@
 | 
			
		||||
namespace App\Factory;
 | 
			
		||||
 | 
			
		||||
use App\Models\User;
 | 
			
		||||
use Illuminate\Support\Str;
 | 
			
		||||
 | 
			
		||||
class UserFactory
 | 
			
		||||
{
 | 
			
		||||
@ -29,6 +30,7 @@ class UserFactory
 | 
			
		||||
        $user->signature = '';
 | 
			
		||||
        $user->theme_id = 0;
 | 
			
		||||
        $user->user_logged_in_notification = true;
 | 
			
		||||
        $user->referral_code = Str::lower(Str::random(32));
 | 
			
		||||
 | 
			
		||||
        return $user;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -64,23 +64,23 @@ class GmailTransport extends AbstractTransport
 | 
			
		||||
 | 
			
		||||
        $body->setRaw($this->base64_encode($bcc_list.$message->toString()));
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
        // try {
 | 
			
		||||
            $service->users_messages->send('me', $body, []);
 | 
			
		||||
        } catch(\Google\Service\Exception $e) {
 | 
			
		||||
            /* Need to slow down */
 | 
			
		||||
            if ($e->getCode() == '429') {
 | 
			
		||||
                nlog("429 google - retrying ");
 | 
			
		||||
        // } catch(\Google\Service\Exception $e) {
 | 
			
		||||
        //     /* Need to slow down */
 | 
			
		||||
        //     if ($e->getCode() == '429') {
 | 
			
		||||
        //         nlog("429 google - retrying ");
 | 
			
		||||
 | 
			
		||||
                sleep(rand(3,8));
 | 
			
		||||
        //         sleep(rand(3,8));
 | 
			
		||||
                
 | 
			
		||||
                try {
 | 
			
		||||
                    $service->users_messages->send('me', $body, []);
 | 
			
		||||
                } catch(\Google\Service\Exception $e) {
 | 
			
		||||
        //         try {
 | 
			
		||||
        //             $service->users_messages->send('me', $body, []);
 | 
			
		||||
        //         } catch(\Google\Service\Exception $e) {
 | 
			
		||||
                
 | 
			
		||||
                }
 | 
			
		||||
        //         }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        //     }
 | 
			
		||||
        // }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function base64_encode($data)
 | 
			
		||||
 | 
			
		||||
@ -38,6 +38,7 @@ use App\Transformers\ArraySerializer;
 | 
			
		||||
use App\Transformers\EntityTransformer;
 | 
			
		||||
use League\Fractal\Resource\Collection;
 | 
			
		||||
use Illuminate\Database\Eloquent\Builder;
 | 
			
		||||
use Invoiceninja\Einvoice\Decoder\Schema;
 | 
			
		||||
use League\Fractal\Serializer\JsonApiSerializer;
 | 
			
		||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
 | 
			
		||||
use Illuminate\Contracts\Container\BindingResolutionException;
 | 
			
		||||
@ -996,8 +997,8 @@ class BaseController extends Controller
 | 
			
		||||
 | 
			
		||||
                if(request()->has('einvoice')){
 | 
			
		||||
 | 
			
		||||
                    // $ro = new Schema();
 | 
			
		||||
                    // $response_data['einvoice_schema'] = $ro('FACT1');
 | 
			
		||||
                    $ro = new Schema();
 | 
			
		||||
                    $response_data['einvoice_schema'] = $ro('FACT1');
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -26,8 +26,6 @@ class SelfUpdateController extends BaseController
 | 
			
		||||
    use ClientGroupSettingsSaver;
 | 
			
		||||
    use AppSetup;
 | 
			
		||||
 | 
			
		||||
    // private bool $use_zip = false;
 | 
			
		||||
 | 
			
		||||
    private string $filename = 'invoiceninja.tar';
 | 
			
		||||
 | 
			
		||||
    private array $purge_file_list = [
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@ namespace App\Http\Controllers;
 | 
			
		||||
 | 
			
		||||
use App\Utils\Statics;
 | 
			
		||||
use Illuminate\Http\Response;
 | 
			
		||||
use Invoiceninja\Einvoice\Decoder\Schema;
 | 
			
		||||
 | 
			
		||||
class StaticController extends BaseController
 | 
			
		||||
{
 | 
			
		||||
@ -60,8 +61,8 @@ class StaticController extends BaseController
 | 
			
		||||
 | 
			
		||||
        if(request()->has('einvoice')){
 | 
			
		||||
            
 | 
			
		||||
            // $schema = new Schema();
 | 
			
		||||
            // $response_data['einvoice_schema'] = $schema('FACT1'); 
 | 
			
		||||
            $schema = new Schema();
 | 
			
		||||
            $response_data['einvoice_schema'] = $schema('FACT1'); 
 | 
			
		||||
        
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,56 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
 *
 | 
			
		||||
 * @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
 *
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Http\Controllers;
 | 
			
		||||
 | 
			
		||||
use App\Libraries\MultiDB;
 | 
			
		||||
use App\Models\Company;
 | 
			
		||||
use App\Models\CompanyGateway;
 | 
			
		||||
use App\Models\User;
 | 
			
		||||
use App\PaymentDrivers\WePayPaymentDriver;
 | 
			
		||||
use App\Utils\Traits\MakesHash;
 | 
			
		||||
use Illuminate\Support\Facades\Cache;
 | 
			
		||||
 | 
			
		||||
class WePayController extends BaseController
 | 
			
		||||
{
 | 
			
		||||
    use MakesHash;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialize WePay Signup.
 | 
			
		||||
     */
 | 
			
		||||
    public function signup(string $token)
 | 
			
		||||
    {
 | 
			
		||||
        // return render('gateways.wepay.signup.finished');
 | 
			
		||||
 | 
			
		||||
        $hash = Cache::get($token);
 | 
			
		||||
 | 
			
		||||
        MultiDB::findAndSetDbByCompanyKey($hash['company_key']);
 | 
			
		||||
 | 
			
		||||
        $user = User::findOrFail($hash['user_id']);
 | 
			
		||||
 | 
			
		||||
        $company = Company::where('company_key', $hash['company_key'])->firstOrFail();
 | 
			
		||||
 | 
			
		||||
        $data['user_id'] = $user->id;
 | 
			
		||||
        $data['user_company'] = $company;
 | 
			
		||||
 | 
			
		||||
        // $data['company_key'] = $company->company_key;
 | 
			
		||||
        // $data['db'] = $company->db;
 | 
			
		||||
 | 
			
		||||
        $wepay_driver = new WePayPaymentDriver(new CompanyGateway(), null, null);
 | 
			
		||||
 | 
			
		||||
        return $wepay_driver->setup($data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function finished()
 | 
			
		||||
    {
 | 
			
		||||
        return render('gateways.wepay.signup.finished');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -35,4 +35,14 @@ class UpdateCompanyUserRequest extends Request
 | 
			
		||||
    {
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function prepareForValidation()
 | 
			
		||||
    {
 | 
			
		||||
        $input = $this->all();
 | 
			
		||||
 | 
			
		||||
        if(isset($input['company_user']['user']))
 | 
			
		||||
            unset($input['company_user']['user']);
 | 
			
		||||
 | 
			
		||||
        $this->replace($input);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -45,6 +45,9 @@ class ConnectNordigenBankIntegrationRequest extends Request
 | 
			
		||||
 | 
			
		||||
        $context = $this->getTokenContent();
 | 
			
		||||
 | 
			
		||||
        if(isset($context['institution_id']))
 | 
			
		||||
            $input['institution_id'] = $context['institution_id'];
 | 
			
		||||
 | 
			
		||||
        $input["redirect"] = isset($context["is_react"]) && $context['is_react'] ? config('ninja.react_url') . "/#/settings/bank_accounts" : config('ninja.app_url');
 | 
			
		||||
 | 
			
		||||
        $this->replace($input);
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@ class GenericReportRequest extends Request
 | 
			
		||||
     */
 | 
			
		||||
    public function authorize(): bool
 | 
			
		||||
    {
 | 
			
		||||
        return $this->checkAuthority();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function rules()
 | 
			
		||||
@ -68,27 +68,25 @@ class GenericReportRequest extends Request
 | 
			
		||||
 | 
			
		||||
        $input['user_id'] = auth()->user()->id;
 | 
			
		||||
 | 
			
		||||
        if(!$this->checkAuthority()){
 | 
			
		||||
            $input['date_range'] = '';            
 | 
			
		||||
            $input['start_date'] = '';
 | 
			
		||||
            $input['end_date'] = '';
 | 
			
		||||
            $input['send_email'] = true;
 | 
			
		||||
            $input['report_keys'] = [];
 | 
			
		||||
            $input['document_email_attachment'] = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->replace($input);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function checkAuthority()
 | 
			
		||||
    {
 | 
			
		||||
        $this->error_message = ctrans('texts.authorization_failure');
 | 
			
		||||
 | 
			
		||||
        /** @var \App\Models\User $user */
 | 
			
		||||
        $user = auth()->user();
 | 
			
		||||
    
 | 
			
		||||
        if(Ninja::isHosted() && $user->account->isFreeHostedClient()){
 | 
			
		||||
            $this->error_message = ctrans('texts.upgrade_to_view_reports');
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $user->isAdmin() || $user->hasPermission('view_reports');
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function failedAuthorization()
 | 
			
		||||
    {
 | 
			
		||||
        throw new AuthorizationException($this->error_message);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -289,6 +289,8 @@ class MatchBankTransactions implements ShouldQueue
 | 
			
		||||
 | 
			
		||||
    private function createPayment($invoices, float $amount): void
 | 
			
		||||
    {
 | 
			
		||||
        
 | 
			
		||||
        $this->attachable_invoices = [];
 | 
			
		||||
        $this->available_balance = $amount;
 | 
			
		||||
 | 
			
		||||
        \DB::connection(config('database.default'))->transaction(function () use ($invoices) {
 | 
			
		||||
 | 
			
		||||
@ -152,19 +152,39 @@ class NinjaMailerJob implements ShouldQueue
 | 
			
		||||
            LightLogs::create(new EmailSuccess($this->nmo->company->company_key, $this->nmo->mailable->subject))
 | 
			
		||||
                ->send();
 | 
			
		||||
 | 
			
		||||
        } catch (\Symfony\Component\Mime\Exception\RfcComplianceException $e) {
 | 
			
		||||
        } 
 | 
			
		||||
        catch (\Symfony\Component\Mime\Exception\RfcComplianceException $e) {
 | 
			
		||||
            nlog("Mailer failed with a Logic Exception {$e->getMessage()}");
 | 
			
		||||
            $this->fail();
 | 
			
		||||
            $this->cleanUpMailers();
 | 
			
		||||
            $this->logMailError($e->getMessage(), $this->company->clients()->first());
 | 
			
		||||
            return;
 | 
			
		||||
        } catch (\Symfony\Component\Mime\Exception\LogicException $e) {
 | 
			
		||||
        } 
 | 
			
		||||
        catch (\Symfony\Component\Mime\Exception\LogicException $e) {
 | 
			
		||||
            nlog("Mailer failed with a Logic Exception {$e->getMessage()}");
 | 
			
		||||
            $this->fail();
 | 
			
		||||
            $this->cleanUpMailers();
 | 
			
		||||
            $this->logMailError($e->getMessage(), $this->company->clients()->first());
 | 
			
		||||
            return;
 | 
			
		||||
        } catch (\Exception | \Google\Service\Exception $e) {
 | 
			
		||||
        }
 | 
			
		||||
        catch(\Symfony\Component\Mailer\Transport\Dsn $e){
 | 
			
		||||
            nlog("Incorrectly configured mail server - setting to default mail driver.");
 | 
			
		||||
            $this->nmo->settings->email_sending_method = 'default';
 | 
			
		||||
            return $this->setMailDriver();
 | 
			
		||||
        }
 | 
			
		||||
        catch(\Google\Service\Exception $e){
 | 
			
		||||
 | 
			
		||||
            if ($e->getCode() == '429') {
 | 
			
		||||
            
 | 
			
		||||
                $message = "Google rate limiting triggered, we are queueing based on Gmail requirements.";
 | 
			
		||||
                $this->logMailError($message, $this->company->clients()->first());
 | 
			
		||||
                sleep(rand(1, 2));
 | 
			
		||||
                $this->release(900);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        
 | 
			
		||||
        } 
 | 
			
		||||
        catch (\Exception $e) {
 | 
			
		||||
            nlog("Mailer failed with {$e->getMessage()}");
 | 
			
		||||
            $message = $e->getMessage();
 | 
			
		||||
 | 
			
		||||
@ -221,8 +241,7 @@ class NinjaMailerJob implements ShouldQueue
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* Releasing immediately does not add in the backoff */
 | 
			
		||||
            sleep(rand(5, 10));
 | 
			
		||||
 | 
			
		||||
            sleep(rand(2, 3));
 | 
			
		||||
            $this->release($this->backoff()[$this->attempts() - 1]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -779,6 +798,7 @@ class NinjaMailerJob implements ShouldQueue
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                $token = json_decode($guzzle->post($url, [
 | 
			
		||||
                    'form_params' => [
 | 
			
		||||
                        'client_id' => config('ninja.o365.client_id'),
 | 
			
		||||
@ -788,6 +808,10 @@ class NinjaMailerJob implements ShouldQueue
 | 
			
		||||
                        'refresh_token' => $user->oauth_user_refresh_token
 | 
			
		||||
                    ],
 | 
			
		||||
                ])->getBody()->getContents());
 | 
			
		||||
            }
 | 
			
		||||
            catch(\Exception $e){
 | 
			
		||||
                nlog("Problem getting new Microsoft token for User: {$user->email}");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($token) {
 | 
			
		||||
                $user->oauth_user_refresh_token = property_exists($token, 'refresh_token') ? $token->refresh_token : $user->oauth_user_refresh_token;
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ use App\Models\User;
 | 
			
		||||
use App\Utils\Ninja;
 | 
			
		||||
use App\Utils\Traits\MakesHash;
 | 
			
		||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
			
		||||
use Illuminate\Support\Str;
 | 
			
		||||
 | 
			
		||||
class CreateUser
 | 
			
		||||
{
 | 
			
		||||
@ -62,6 +63,7 @@ class CreateUser
 | 
			
		||||
        $user->fill($this->request);
 | 
			
		||||
        $user->email = $this->request['email']; //todo need to remove this in production
 | 
			
		||||
        $user->last_login = now();
 | 
			
		||||
        $user->referral_code = Str::lower(Str::random(32));
 | 
			
		||||
        $user->ip = request()->ip();
 | 
			
		||||
 | 
			
		||||
        if (Ninja::isSelfHost()) {
 | 
			
		||||
 | 
			
		||||
@ -1,215 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
 *
 | 
			
		||||
 * @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
 *
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Livewire;
 | 
			
		||||
 | 
			
		||||
use App\DataMapper\FeesAndLimits;
 | 
			
		||||
use App\Factory\CompanyGatewayFactory;
 | 
			
		||||
use App\Libraries\MultiDB;
 | 
			
		||||
use App\Models\Company;
 | 
			
		||||
use App\Models\CompanyGateway;
 | 
			
		||||
use App\Models\GatewayType;
 | 
			
		||||
use App\Models\User;
 | 
			
		||||
use App\PaymentDrivers\WePayPaymentDriver;
 | 
			
		||||
use Livewire\Component;
 | 
			
		||||
use WePay;
 | 
			
		||||
 | 
			
		||||
class WepaySignup extends Component
 | 
			
		||||
{
 | 
			
		||||
    public $user;
 | 
			
		||||
 | 
			
		||||
    public $user_id;
 | 
			
		||||
 | 
			
		||||
    public $company_key;
 | 
			
		||||
 | 
			
		||||
    public $first_name;
 | 
			
		||||
 | 
			
		||||
    public $last_name;
 | 
			
		||||
 | 
			
		||||
    public $email;
 | 
			
		||||
 | 
			
		||||
    public $company_name;
 | 
			
		||||
 | 
			
		||||
    public $country;
 | 
			
		||||
 | 
			
		||||
    public $ach;
 | 
			
		||||
 | 
			
		||||
    public $wepay_payment_tos_agree;
 | 
			
		||||
 | 
			
		||||
    public $debit_cards;
 | 
			
		||||
 | 
			
		||||
    public $terms;
 | 
			
		||||
 | 
			
		||||
    public $privacy_policy;
 | 
			
		||||
 | 
			
		||||
    public $saved;
 | 
			
		||||
 | 
			
		||||
    public Company $company;
 | 
			
		||||
 | 
			
		||||
    protected $rules = [
 | 
			
		||||
        'first_name' => ['required'],
 | 
			
		||||
        'last_name' => ['required'],
 | 
			
		||||
        'email' => ['required', 'email'],
 | 
			
		||||
        'company_name' => ['required'],
 | 
			
		||||
        'country' => ['required'],
 | 
			
		||||
        'ach' => ['sometimes'],
 | 
			
		||||
        'wepay_payment_tos_agree' => ['accepted'],
 | 
			
		||||
        'debit_cards' => ['sometimes'],
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    public function mount()
 | 
			
		||||
    {
 | 
			
		||||
        MultiDB::setDb($this->company->db);
 | 
			
		||||
 | 
			
		||||
        $user = User::find($this->user_id);
 | 
			
		||||
        $this->company = Company::query()->where('company_key', $this->company->company_key)->first();
 | 
			
		||||
 | 
			
		||||
        $this->fill([
 | 
			
		||||
            'wepay_payment_tos_agree' => '',
 | 
			
		||||
            'ach' => '',
 | 
			
		||||
            'country' => 'US',
 | 
			
		||||
            'user' => $user,
 | 
			
		||||
            'first_name' => $user->first_name,
 | 
			
		||||
            'last_name' => $user->last_name,
 | 
			
		||||
            'email' => $user->email,
 | 
			
		||||
            'company_name' => $this->company->present()->name(),
 | 
			
		||||
            'saved' => ctrans('texts.confirm'),
 | 
			
		||||
            'terms' => '<a href="https://go.wepay.com/terms-of-service" target="_blank">'.ctrans('texts.terms_of_service').'</a>',
 | 
			
		||||
            'privacy_policy' => '<a href="https://go.wepay.com/privacy-policy" target="_blank">'.ctrans('texts.privacy_policy').'</a>',
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function render()
 | 
			
		||||
    {
 | 
			
		||||
        return render('gateways.wepay.signup.wepay-signup');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function submit()
 | 
			
		||||
    {
 | 
			
		||||
        MultiDB::setDb($this->company->db);
 | 
			
		||||
 | 
			
		||||
        $data = $this->validate($this->rules);
 | 
			
		||||
 | 
			
		||||
        //need to create or get a new WePay CompanyGateway
 | 
			
		||||
        $cg = CompanyGateway::query()->where('gateway_key', '8fdeed552015b3c7b44ed6c8ebd9e992')
 | 
			
		||||
                            ->where('company_id', $this->company->id)
 | 
			
		||||
                            ->firstOrNew();
 | 
			
		||||
 | 
			
		||||
        if (! $cg->id) {
 | 
			
		||||
            $fees_and_limits = new \stdClass();
 | 
			
		||||
            $fees_and_limits->{GatewayType::CREDIT_CARD} = new FeesAndLimits();
 | 
			
		||||
            $fees_and_limits->{GatewayType::BANK_TRANSFER} = new FeesAndLimits();
 | 
			
		||||
 | 
			
		||||
            $cg = CompanyGatewayFactory::create($this->company->id, $this->user->id);
 | 
			
		||||
            $cg->gateway_key = '8fdeed552015b3c7b44ed6c8ebd9e992';
 | 
			
		||||
            $cg->require_cvv = false;
 | 
			
		||||
            $cg->require_billing_address = false;
 | 
			
		||||
            $cg->require_shipping_address = false;
 | 
			
		||||
            $cg->update_details = false;
 | 
			
		||||
            $cg->config = encrypt(config('ninja.testvars.checkout'));
 | 
			
		||||
            $cg->fees_and_limits = $fees_and_limits;
 | 
			
		||||
            $cg->token_billing = 'always';
 | 
			
		||||
            $cg->save();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->saved = ctrans('texts.processing');
 | 
			
		||||
 | 
			
		||||
        $wepay_driver = new WePayPaymentDriver($cg, null, null);
 | 
			
		||||
 | 
			
		||||
        $wepay = $wepay_driver->init()->wepay;
 | 
			
		||||
 | 
			
		||||
        $user_details = [
 | 
			
		||||
            'client_id' => config('ninja.wepay.client_id'),
 | 
			
		||||
            'client_secret' => config('ninja.wepay.client_secret'),
 | 
			
		||||
            'email' => $data['email'],
 | 
			
		||||
            'first_name' => $data['first_name'],
 | 
			
		||||
            'last_name' => $data['last_name'],
 | 
			
		||||
            'original_ip' => request()->ip(),
 | 
			
		||||
            'original_device' => request()->server('HTTP_USER_AGENT'),
 | 
			
		||||
            'tos_acceptance_time' => time(),
 | 
			
		||||
            'redirect_uri' => route('wepay.finished'),
 | 
			
		||||
            'scope' => 'manage_accounts,collect_payments,view_user,preapprove_payments,send_money',
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $wepay_user = $wepay->request('user/register/', $user_details);
 | 
			
		||||
 | 
			
		||||
        $access_token = $wepay_user->access_token;
 | 
			
		||||
 | 
			
		||||
        $access_token_expires = $wepay_user->expires_in ? (time() + $wepay_user->expires_in) : null;
 | 
			
		||||
 | 
			
		||||
        $wepay = new WePay($access_token);
 | 
			
		||||
 | 
			
		||||
        $account_details = [
 | 
			
		||||
            'name' => $data['company_name'],
 | 
			
		||||
            'description' => ctrans('texts.wepay_account_description'),
 | 
			
		||||
            'theme_object' => json_decode('{"name":"Invoice Ninja","primary_color":"0b4d78","secondary_color":"0b4d78","background_color":"f8f8f8","button_color":"33b753"}'),
 | 
			
		||||
            'callback_uri' => route('payment_webhook', ['company_key' => $this->company->company_key, 'company_gateway_id' => $cg->hashed_id]),
 | 
			
		||||
            'rbits' => $this->company->rBits(),
 | 
			
		||||
            'country' => $data['country'],
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        if ($data['country'] == 'CA') {
 | 
			
		||||
            $account_details['currencies'] = ['CAD'];
 | 
			
		||||
            $account_details['country_options'] = ['debit_opt_in' => boolval($data['debit_cards'])];
 | 
			
		||||
        } elseif ($data['country'] == 'GB') {
 | 
			
		||||
            $account_details['currencies'] = ['GBP'];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $wepay_account = $wepay->request('account/create/', $account_details);
 | 
			
		||||
        $confirmation_required = false;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $wepay->request('user/send_confirmation/', []);
 | 
			
		||||
            $confirmation_required = true;
 | 
			
		||||
        } catch (\WePayException $ex) {
 | 
			
		||||
            if ($ex->getMessage() == 'This access_token is already approved.') {
 | 
			
		||||
                $confirmation_required = false;
 | 
			
		||||
            } else {
 | 
			
		||||
 | 
			
		||||
                /** @phpstan-ignore-next-line */
 | 
			
		||||
                request()->session()->flash('message', $ex->getMessage());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            nlog('failed in try catch ');
 | 
			
		||||
            nlog($ex->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $config = [
 | 
			
		||||
            'userId' => $wepay_user->user_id,
 | 
			
		||||
            'accessToken' => $access_token,
 | 
			
		||||
            'tokenType' => $wepay_user->token_type,
 | 
			
		||||
            'tokenExpires' => $access_token_expires,
 | 
			
		||||
            'accountId' => $wepay_account->account_id,
 | 
			
		||||
            'state' => $wepay_account->state,
 | 
			
		||||
            'testMode' => config('ninja.wepay.environment') == 'staging',
 | 
			
		||||
            'country' => $data['country'],
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $cg->setConfig($config);
 | 
			
		||||
        $cg->save();
 | 
			
		||||
 | 
			
		||||
        if ($confirmation_required) {
 | 
			
		||||
 | 
			
		||||
            /** @phpstan-ignore-next-line **/
 | 
			
		||||
            request()->session()->flash('message', trans('texts.created_wepay_confirmation_required'));
 | 
			
		||||
        } else {
 | 
			
		||||
            $update_uri = $wepay->request('/account/get_update_uri', [
 | 
			
		||||
                'account_id' => $wepay_account->account_id,
 | 
			
		||||
                'redirect_uri' => config('ninja.app_url'),
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
            return redirect($update_uri->uri);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return redirect()->to('/wepay/finished');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -44,4 +44,15 @@ class ClientContactPresenter extends EntityPresenter
 | 
			
		||||
    {
 | 
			
		||||
        return $this->name().' <'.$this->entity->email.'>' ?? '';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function phone()
 | 
			
		||||
    {
 | 
			
		||||
        return strlen($this->phone ?? '') > 1 ? $this->phone : '';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function email()
 | 
			
		||||
    {
 | 
			
		||||
        return strlen($this->email ?? '') > 1 ? $this->email : '';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,79 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
 *
 | 
			
		||||
 * @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
 *
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Notifications\Ninja;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Notifications\Messages\SlackMessage;
 | 
			
		||||
use Illuminate\Notifications\Notification;
 | 
			
		||||
 | 
			
		||||
class WePayFailureNotification extends Notification
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new notification instance.
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    protected $company_id;
 | 
			
		||||
 | 
			
		||||
    public function __construct($company_id)
 | 
			
		||||
    {
 | 
			
		||||
        $this->company_id = $company_id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the notification's delivery channels.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  mixed  $notifiable
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function via($notifiable)
 | 
			
		||||
    {
 | 
			
		||||
        return ['slack'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the mail representation of the notification.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  mixed  $notifiable
 | 
			
		||||
     *
 | 
			
		||||
     */
 | 
			
		||||
    public function toMail($notifiable)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the array representation of the notification.
 | 
			
		||||
     *
 | 
			
		||||
     * @param  mixed  $notifiable
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function toArray($notifiable)
 | 
			
		||||
    {
 | 
			
		||||
        return [
 | 
			
		||||
            //
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function toSlack($notifiable)
 | 
			
		||||
    {
 | 
			
		||||
        $ip = '';
 | 
			
		||||
 | 
			
		||||
        if (request()) {
 | 
			
		||||
            $ip = request()->getClientIp();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return  (new SlackMessage())
 | 
			
		||||
                ->success()
 | 
			
		||||
                ->from(ctrans('texts.notification_bot'))
 | 
			
		||||
                ->image('https://app.invoiceninja.com/favicon.png')
 | 
			
		||||
                ->content("New WePay ACH Failure from Company ID: {$this->company_id} IP: {$ip}");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -45,7 +45,7 @@
 | 
			
		||||
    @include('portal.ninja2020.gateways.includes.save_card')
 | 
			
		||||
 | 
			
		||||
    <!-- This include pops up a credit card form -->
 | 
			
		||||
    @include('portal.ninja2020.gateways.wepay.includes.credit_card')
 | 
			
		||||
    @include('portal.ninja2020.gateways.stripe.includes.credit_card')
 | 
			
		||||
 | 
			
		||||
    @include('portal.ninja2020.gateways.includes.pay_now')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,354 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
 *
 | 
			
		||||
 * @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
 *
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\PaymentDrivers\WePay;
 | 
			
		||||
 | 
			
		||||
use App\Exceptions\PaymentFailed;
 | 
			
		||||
use App\Jobs\Util\SystemLogger;
 | 
			
		||||
use App\Models\ClientGatewayToken;
 | 
			
		||||
use App\Models\GatewayType;
 | 
			
		||||
use App\Models\Payment;
 | 
			
		||||
use App\Models\SystemLog;
 | 
			
		||||
use App\Notifications\Ninja\WePayFailureNotification;
 | 
			
		||||
use App\PaymentDrivers\WePayPaymentDriver;
 | 
			
		||||
use App\Utils\Traits\MakesHash;
 | 
			
		||||
use Illuminate\Http\Request;
 | 
			
		||||
use Illuminate\Support\Str;
 | 
			
		||||
 | 
			
		||||
class ACH
 | 
			
		||||
{
 | 
			
		||||
    use MakesHash;
 | 
			
		||||
    use WePayCommon;
 | 
			
		||||
 | 
			
		||||
    public $wepay_payment_driver;
 | 
			
		||||
 | 
			
		||||
    public function __construct(WePayPaymentDriver $wepay_payment_driver)
 | 
			
		||||
    {
 | 
			
		||||
        $this->wepay_payment_driver = $wepay_payment_driver;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function authorizeView($data)
 | 
			
		||||
    {
 | 
			
		||||
        $data['gateway'] = $this->wepay_payment_driver;
 | 
			
		||||
        $data['country_code'] = $this->wepay_payment_driver?->client?->country ? $this->wepay_payment_driver->client->country->iso_3166_2 : $this->wepay_payment_driver->company_gateway->company->country()->iso_3166_2;
 | 
			
		||||
 | 
			
		||||
        return render('gateways.wepay.authorize.bank_transfer', $data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function authorizeResponse($request)
 | 
			
		||||
    {
 | 
			
		||||
        //https://developer.wepay.com/api/api-calls/credit_card#authorize
 | 
			
		||||
        $data = $request->all();
 | 
			
		||||
        // authorize the credit card
 | 
			
		||||
 | 
			
		||||
        //nlog($data);
 | 
			
		||||
        /*
 | 
			
		||||
          '_token' => '1Fk5CRj34up5ntKPvrFyMIAJhDdUNF3boqT3iIN3',
 | 
			
		||||
          'company_gateway_id' => '39',
 | 
			
		||||
          'payment_method_id' => '1',
 | 
			
		||||
          'gateway_response' => NULL,
 | 
			
		||||
          'is_default' => NULL,
 | 
			
		||||
          'credit_card_id' => '180642154638',
 | 
			
		||||
          'q' => '/client/payment_methods',
 | 
			
		||||
          'method' => '1',
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $response = $this->wepay_payment_driver->wepay->request('payment_bank/persist', [
 | 
			
		||||
                'client_id'          => config('ninja.wepay.client_id'),
 | 
			
		||||
                'client_secret'      => config('ninja.wepay.client_secret'),
 | 
			
		||||
                'payment_bank_id'     => (int) $data['bank_account_id'],
 | 
			
		||||
            ]);
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            $this->wepay_payment_driver->sendFailureMail($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            $message = [
 | 
			
		||||
                'server_response' => $e->getMessage(),
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            SystemLogger::dispatch(
 | 
			
		||||
                $e->getMessage(),
 | 
			
		||||
                SystemLog::CATEGORY_GATEWAY_RESPONSE,
 | 
			
		||||
                SystemLog::EVENT_GATEWAY_FAILURE,
 | 
			
		||||
                SystemLog::TYPE_WEPAY,
 | 
			
		||||
                $this->wepay_payment_driver->client,
 | 
			
		||||
                $this->wepay_payment_driver->client->company,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            if (config('ninja.notification.slack')) {
 | 
			
		||||
                $this->wepay_payment_driver->company_gateway->company->notification(new WePayFailureNotification($this->wepay_payment_driver->company_gateway->company))->ninja();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            throw new PaymentFailed($e->getMessage(), 400);
 | 
			
		||||
        }
 | 
			
		||||
        // display the response
 | 
			
		||||
        // nlog($response);
 | 
			
		||||
 | 
			
		||||
        if (in_array($response->state, ['new', 'pending', 'authorized'])) {
 | 
			
		||||
            $this->storePaymentMethod($response, GatewayType::BANK_TRANSFER);
 | 
			
		||||
 | 
			
		||||
            return redirect()->route('client.payment_methods.index');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        throw new PaymentFailed('There was a problem adding this payment method.', 400);
 | 
			
		||||
        /*
 | 
			
		||||
            {
 | 
			
		||||
                "payment_bank_id": 12345,
 | 
			
		||||
                "bank_name": "Wells Fargo",
 | 
			
		||||
                "account_last_four": "6789",
 | 
			
		||||
                "state": "authorized"
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            state options: new, pending, authorized, disabled.
 | 
			
		||||
        */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If the bank transfer token is PENDING - we need to verify!! */
 | 
			
		||||
    //
 | 
			
		||||
 | 
			
		||||
    public function verificationView(ClientGatewayToken $token)
 | 
			
		||||
    {
 | 
			
		||||
        $this->wepay_payment_driver->init();
 | 
			
		||||
 | 
			
		||||
        $data = [
 | 
			
		||||
            'token' => $token,
 | 
			
		||||
            'gateway' => $this->wepay_payment_driver,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        return render('gateways.wepay.authorize.verify', $data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
    {
 | 
			
		||||
    "client_id": 1234,
 | 
			
		||||
    "client_secret": "b1fc2f68-4d1f-4a",
 | 
			
		||||
    "payment_bank_id": 12345,
 | 
			
		||||
    "type": "microdeposits",
 | 
			
		||||
    "microdeposits": [
 | 
			
		||||
        8,
 | 
			
		||||
        12
 | 
			
		||||
    ]
 | 
			
		||||
    }
 | 
			
		||||
     */
 | 
			
		||||
    public function processVerification(Request $request, ClientGatewayToken $token)
 | 
			
		||||
    {
 | 
			
		||||
        $transactions = $request->input('transactions');
 | 
			
		||||
 | 
			
		||||
        $transformed_transactions = [];
 | 
			
		||||
 | 
			
		||||
        foreach ($transactions as $transaction) {
 | 
			
		||||
            $transformed_transactions[] = (int) $transaction;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $response = $this->wepay_payment_driver->wepay->request('payment_bank/verify', [
 | 
			
		||||
                'client_id'          => config('ninja.wepay.client_id'),
 | 
			
		||||
                'client_secret'      => config('ninja.wepay.client_secret'),
 | 
			
		||||
                'payment_bank_id'    => $token->token,
 | 
			
		||||
                'type'               => 'microdeposits',
 | 
			
		||||
                'microdeposits'      => $transformed_transactions,
 | 
			
		||||
            ]);
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            nlog('we pay exception');
 | 
			
		||||
            nlog($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            return redirect()->route('client.payment_methods.verification', ['payment_method' => $token->hashed_id, 'method' => GatewayType::BANK_TRANSFER])
 | 
			
		||||
                      ->with('error', $e->getMessage());
 | 
			
		||||
        }
 | 
			
		||||
        /*
 | 
			
		||||
        {
 | 
			
		||||
            "payment_bank_id": 12345,
 | 
			
		||||
            "bank_name": "Wells Fargo",
 | 
			
		||||
            "account_last_four": "6789",
 | 
			
		||||
            "state": "authorized"
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
        nlog($response);
 | 
			
		||||
 | 
			
		||||
        //$meta = $token->meta;
 | 
			
		||||
        if ($response->state == 'authorized') {
 | 
			
		||||
            $meta = $token->meta;
 | 
			
		||||
            $meta->state = $response->state;
 | 
			
		||||
            $token->meta = $meta;
 | 
			
		||||
            $token->save();
 | 
			
		||||
 | 
			
		||||
            return redirect()->route('client.payment_methods.index');
 | 
			
		||||
        } else {
 | 
			
		||||
            return redirect()->route('client.payment_methods.verification', ['payment_method' => $token->hashed_id, 'method' => GatewayType::BANK_TRANSFER])
 | 
			
		||||
                      ->with('error', ctrans('texts.verification_failed'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ///////////////////////////////////////////////////////////////////////////////////////
 | 
			
		||||
    public function paymentView(array $data)
 | 
			
		||||
    {
 | 
			
		||||
        $data['gateway'] = $this->wepay_payment_driver;
 | 
			
		||||
        $data['currency'] = $this->wepay_payment_driver->client->getCurrencyCode();
 | 
			
		||||
        $data['payment_method_id'] = GatewayType::BANK_TRANSFER;
 | 
			
		||||
        $data['amount'] = $data['total']['amount_with_fee'];
 | 
			
		||||
 | 
			
		||||
        return render('gateways.wepay.bank_transfer', $data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function paymentResponse($request)
 | 
			
		||||
    {
 | 
			
		||||
        $token = ClientGatewayToken::query()->find($this->decodePrimaryKey($request->input('source')));
 | 
			
		||||
        $token_meta = $token->meta;
 | 
			
		||||
 | 
			
		||||
        if (! property_exists($token_meta, 'state') || $token_meta->state != 'authorized') {
 | 
			
		||||
            $response = $this->wepay_payment_driver->wepay->request('/payment_bank', [
 | 
			
		||||
                'client_id'          => config('ninja.wepay.client_id'),
 | 
			
		||||
                'client_secret'      => config('ninja.wepay.client_secret'),
 | 
			
		||||
                'payment_bank_id'    => $token->token,
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
            if ($response->state == 'authorized') {
 | 
			
		||||
                $meta = $token->meta;
 | 
			
		||||
                $meta->state = $response->state;
 | 
			
		||||
                $token->meta = $meta;
 | 
			
		||||
                $token->save();
 | 
			
		||||
            } else {
 | 
			
		||||
                return redirect()->route('client.payment_methods.verification', ['payment_method' => $token->hashed_id, 'method' => GatewayType::BANK_TRANSFER]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $app_fee = (config('ninja.wepay.fee_ach_multiplier') * $this->wepay_payment_driver->payment_hash->data->amount_with_fee) + config('ninja.wepay.fee_fixed');
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $response = $this->wepay_payment_driver->wepay->request('checkout/create', [
 | 
			
		||||
                // 'callback_uri'        => route('payment_webhook', ['company_key' => $this->wepay_payment_driver->company_gateway->company->company_key, 'company_gateway_id' => $this->wepay_payment_driver->company_gateway->hashed_id]),
 | 
			
		||||
                'unique_id'           => Str::random(40),
 | 
			
		||||
                'account_id'          => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'),
 | 
			
		||||
                'amount'              => $this->wepay_payment_driver->payment_hash->data->amount_with_fee,
 | 
			
		||||
                'currency'            => $this->wepay_payment_driver->client->getCurrencyCode(),
 | 
			
		||||
                'short_description'   => 'Goods and Services',
 | 
			
		||||
                'type'                => 'goods',
 | 
			
		||||
                'fee'                 => [
 | 
			
		||||
                    'fee_payer' => config('ninja.wepay.fee_payer'),
 | 
			
		||||
                    'app_fee' => $app_fee,
 | 
			
		||||
                ],
 | 
			
		||||
                'payment_method'      => [
 | 
			
		||||
                    'type'            => 'payment_bank',
 | 
			
		||||
                    'payment_bank'     => [
 | 
			
		||||
                        'id'          => $token->token,
 | 
			
		||||
                    ],
 | 
			
		||||
                ],
 | 
			
		||||
            ]);
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            throw new PaymentFailed($e->getMessage(), 500);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Merge all data and store in the payment hash*/
 | 
			
		||||
        $state = [
 | 
			
		||||
            'server_response' => $response,
 | 
			
		||||
            'payment_hash' => $request->payment_hash,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $state = array_merge($state, $request->all());
 | 
			
		||||
        $this->wepay_payment_driver->payment_hash->data = array_merge((array) $this->wepay_payment_driver->payment_hash->data, $state);
 | 
			
		||||
        $this->wepay_payment_driver->payment_hash->save();
 | 
			
		||||
 | 
			
		||||
        if (in_array($response->state, ['authorized', 'captured'])) {
 | 
			
		||||
            //success
 | 
			
		||||
            nlog('success');
 | 
			
		||||
            $payment_status = $response->state == 'authorized' ? Payment::STATUS_COMPLETED : Payment::STATUS_PENDING;
 | 
			
		||||
 | 
			
		||||
            return $this->processSuccessfulPayment($response, $payment_status, GatewayType::BANK_TRANSFER);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (in_array($response->state, ['released', 'cancelled', 'failed', 'expired'])) {
 | 
			
		||||
            //some type of failure
 | 
			
		||||
            nlog('failure');
 | 
			
		||||
 | 
			
		||||
            $payment_status = $response->state == 'cancelled' ? Payment::STATUS_CANCELLED : Payment::STATUS_FAILED;
 | 
			
		||||
 | 
			
		||||
            $this->processUnSuccessfulPayment($response, $payment_status);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function storePaymentMethod($response, $payment_method_id)
 | 
			
		||||
    {
 | 
			
		||||
        $payment_meta = new \stdClass();
 | 
			
		||||
        $payment_meta->exp_month = (string) '';
 | 
			
		||||
        $payment_meta->exp_year = (string) '';
 | 
			
		||||
        $payment_meta->brand = (string) $response->bank_name;
 | 
			
		||||
        $payment_meta->last4 = (string) $response->account_last_four;
 | 
			
		||||
        $payment_meta->type = GatewayType::BANK_TRANSFER;
 | 
			
		||||
        $payment_meta->state = $response->state;
 | 
			
		||||
 | 
			
		||||
        $data = [
 | 
			
		||||
            'payment_meta' => $payment_meta,
 | 
			
		||||
            'token' => $response->payment_bank_id,
 | 
			
		||||
            'payment_method_id' => $payment_method_id,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $this->wepay_payment_driver->storeGatewayToken($data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function tokenBilling($token, $payment_hash)
 | 
			
		||||
    {
 | 
			
		||||
        $token_meta = $token->meta;
 | 
			
		||||
 | 
			
		||||
        if (! property_exists($token_meta, 'state') || $token_meta->state != 'authorized') {
 | 
			
		||||
            return redirect()->route('client.payment_methods.verification', ['payment_method' => $token->hashed_id, 'method' => GatewayType::BANK_TRANSFER]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $amount = array_sum(array_column($this->wepay_payment_driver->payment_hash->invoices(), 'amount')) + $this->wepay_payment_driver->payment_hash->fee_total;
 | 
			
		||||
 | 
			
		||||
        $app_fee = (config('ninja.wepay.fee_cc_multiplier') * $amount) + config('ninja.wepay.fee_fixed');
 | 
			
		||||
 | 
			
		||||
        $response = $this->wepay_payment_driver->wepay->request('checkout/create', [
 | 
			
		||||
            'unique_id'           => Str::random(40),
 | 
			
		||||
            'account_id'          => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'),
 | 
			
		||||
            'amount'              => $amount,
 | 
			
		||||
            'currency'            => $this->wepay_payment_driver->client->getCurrencyCode(),
 | 
			
		||||
            'short_description'   => 'Goods and Services',
 | 
			
		||||
            'type'                => 'goods',
 | 
			
		||||
            'fee'                 => [
 | 
			
		||||
                'fee_payer' => config('ninja.wepay.fee_payer'),
 | 
			
		||||
                'app_fee' => $app_fee,
 | 
			
		||||
            ],
 | 
			
		||||
            'payment_method'      => [
 | 
			
		||||
                'type'            => 'payment_bank',
 | 
			
		||||
                'payment_bank'     => [
 | 
			
		||||
                    'id'          => $token->token,
 | 
			
		||||
                ],
 | 
			
		||||
            ],
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        /* Merge all data and store in the payment hash*/
 | 
			
		||||
        $state = [
 | 
			
		||||
            'server_response' => $response,
 | 
			
		||||
            'payment_hash' => $this->wepay_payment_driver->payment_hash,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $this->wepay_payment_driver->payment_hash->data = array_merge((array) $this->wepay_payment_driver->payment_hash->data, $state);
 | 
			
		||||
        $this->wepay_payment_driver->payment_hash->save();
 | 
			
		||||
 | 
			
		||||
        if (in_array($response->state, ['authorized', 'captured'])) {
 | 
			
		||||
            //success
 | 
			
		||||
            nlog('success');
 | 
			
		||||
            $payment_status = $response->state == 'authorized' ? Payment::STATUS_COMPLETED : Payment::STATUS_PENDING;
 | 
			
		||||
 | 
			
		||||
            return $this->processSuccessfulPayment($response, $payment_status, GatewayType::BANK_TRANSFER, true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (in_array($response->state, ['released', 'cancelled', 'failed', 'expired'])) {
 | 
			
		||||
            //some type of failure
 | 
			
		||||
            nlog('failure');
 | 
			
		||||
 | 
			
		||||
            $payment_status = $response->state == 'cancelled' ? Payment::STATUS_CANCELLED : Payment::STATUS_FAILED;
 | 
			
		||||
 | 
			
		||||
            $this->processUnSuccessfulPayment($response, $payment_status);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,346 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
 *
 | 
			
		||||
 * @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
 *
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\PaymentDrivers\WePay;
 | 
			
		||||
 | 
			
		||||
use App\Exceptions\PaymentFailed;
 | 
			
		||||
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
 | 
			
		||||
use App\Jobs\Util\SystemLogger;
 | 
			
		||||
use App\Models\GatewayType;
 | 
			
		||||
use App\Models\Payment;
 | 
			
		||||
use App\Models\SystemLog;
 | 
			
		||||
use App\PaymentDrivers\WePayPaymentDriver;
 | 
			
		||||
use Illuminate\Support\Str;
 | 
			
		||||
 | 
			
		||||
class CreditCard
 | 
			
		||||
{
 | 
			
		||||
    use WePayCommon;
 | 
			
		||||
 | 
			
		||||
    public $wepay_payment_driver;
 | 
			
		||||
 | 
			
		||||
    public function __construct(WePayPaymentDriver $wepay_payment_driver)
 | 
			
		||||
    {
 | 
			
		||||
        $this->wepay_payment_driver = $wepay_payment_driver;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function authorizeView($data)
 | 
			
		||||
    {
 | 
			
		||||
        $data['gateway'] = $this->wepay_payment_driver;
 | 
			
		||||
        $data['country_code'] = $this->wepay_payment_driver?->client?->country ? $this->wepay_payment_driver->client->country->iso_3166_2 : $this->wepay_payment_driver->company_gateway->company->country()->iso_3166_2;
 | 
			
		||||
 | 
			
		||||
        return render('gateways.wepay.authorize.authorize', $data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function authorizeResponse($request)
 | 
			
		||||
    {
 | 
			
		||||
        //https://developer.wepay.com/api/api-calls/credit_card#authorize
 | 
			
		||||
        $data = $request->all();
 | 
			
		||||
        // authorize the credit card
 | 
			
		||||
 | 
			
		||||
        // nlog($data);
 | 
			
		||||
        /*
 | 
			
		||||
          '_token' => '1Fk5CRj34up5ntKPvrFyMIAJhDdUNF3boqT3iIN3',
 | 
			
		||||
          'company_gateway_id' => '39',
 | 
			
		||||
          'payment_method_id' => '1',
 | 
			
		||||
          'gateway_response' => NULL,
 | 
			
		||||
          'is_default' => NULL,
 | 
			
		||||
          'credit_card_id' => '180642154638',
 | 
			
		||||
          'q' => '/client/payment_methods',
 | 
			
		||||
          'method' => '1',
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $response = $this->wepay_payment_driver->wepay->request('credit_card/authorize', [
 | 
			
		||||
                'client_id'          => config('ninja.wepay.client_id'),
 | 
			
		||||
                'client_secret'      => config('ninja.wepay.client_secret'),
 | 
			
		||||
                'credit_card_id'     => (int) $data['credit_card_id'],
 | 
			
		||||
            ]);
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            return $this->wepay_payment_driver->processInternallyFailedPayment($this->wepay_payment_driver, $e);
 | 
			
		||||
        }
 | 
			
		||||
        // display the response
 | 
			
		||||
        // nlog($response);
 | 
			
		||||
 | 
			
		||||
        if (in_array($response->state, ['new', 'authorized'])) {
 | 
			
		||||
            $this->storePaymentMethod($response, GatewayType::CREDIT_CARD);
 | 
			
		||||
 | 
			
		||||
            return redirect()->route('client.payment_methods.index');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        throw new PaymentFailed('There was a problem adding this payment method.', 400);
 | 
			
		||||
        /*
 | 
			
		||||
            [credit_card_id] => 348084962473
 | 
			
		||||
            [credit_card_name] => Visa xxxxxx4018
 | 
			
		||||
            [state] => authorized
 | 
			
		||||
            [user_name] => Joey Diaz
 | 
			
		||||
            [email] => user@example.com
 | 
			
		||||
            [create_time] => 1623798172
 | 
			
		||||
            [expiration_month] => 10
 | 
			
		||||
            [expiration_year] => 2023
 | 
			
		||||
            [last_four] => 4018
 | 
			
		||||
            [input_source] => card_keyed
 | 
			
		||||
            [virtual_terminal_mode] => none
 | 
			
		||||
            [card_on_file] =>
 | 
			
		||||
            [recurring] =>
 | 
			
		||||
            [cvv_provided] => 1
 | 
			
		||||
            [auto_update] =>
 | 
			
		||||
        */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function paymentView(array $data)
 | 
			
		||||
    {
 | 
			
		||||
        $data['gateway'] = $this->wepay_payment_driver;
 | 
			
		||||
        $data['description'] = ctrans('texts.invoices').': '.collect($data['invoices'])->pluck('invoice_number');
 | 
			
		||||
        $data['country_code'] = $this->wepay_payment_driver?->client?->country ? $this->wepay_payment_driver->client->country->iso_3166_2 : $this->wepay_payment_driver->company_gateway->company()->iso_3166_2;
 | 
			
		||||
 | 
			
		||||
        return render('gateways.wepay.credit_card.pay', $data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function paymentResponse(PaymentResponseRequest $request)
 | 
			
		||||
    {
 | 
			
		||||
        nlog('payment response');
 | 
			
		||||
 | 
			
		||||
        //it could be an existing token or a new credit_card_id that needs to be converted into a wepay token
 | 
			
		||||
        if ($request->has('credit_card_id') && $request->input('credit_card_id')) {
 | 
			
		||||
            nlog('authorize the card first!');
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                $response = $this->wepay_payment_driver->wepay->request('credit_card/authorize', [
 | 
			
		||||
                    'client_id'          => config('ninja.wepay.client_id'),
 | 
			
		||||
                    'client_secret'      => config('ninja.wepay.client_secret'),
 | 
			
		||||
                    'credit_card_id'     => (int) $request->input('credit_card_id'),
 | 
			
		||||
                ]);
 | 
			
		||||
            } catch (\Exception $e) {
 | 
			
		||||
                return $this->wepay_payment_driver->processInternallyFailedPayment($this->wepay_payment_driver, $e);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $credit_card_id = (int) $response->credit_card_id;
 | 
			
		||||
 | 
			
		||||
            if (in_array($response->state, ['new', 'authorized']) && boolval($request->input('store_card'))) {
 | 
			
		||||
                $this->storePaymentMethod($response, GatewayType::CREDIT_CARD);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            $credit_card_id = (int) $request->input('token');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // USD, CAD, and GBP.
 | 
			
		||||
        // nlog($request->all());
 | 
			
		||||
 | 
			
		||||
        $app_fee = (config('ninja.wepay.fee_cc_multiplier') * $this->wepay_payment_driver->payment_hash->data->amount_with_fee) + config('ninja.wepay.fee_fixed');
 | 
			
		||||
        // charge the credit card
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $response = $this->wepay_payment_driver->wepay->request('checkout/create', [
 | 
			
		||||
                'unique_id'           => Str::random(40),
 | 
			
		||||
                'account_id'          => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'),
 | 
			
		||||
                'amount'              => $this->wepay_payment_driver->payment_hash->data->amount_with_fee,
 | 
			
		||||
                'currency'            => $this->wepay_payment_driver->client->getCurrencyCode(),
 | 
			
		||||
                'short_description'   => 'Goods and services',
 | 
			
		||||
                'type'                => 'goods',
 | 
			
		||||
                'fee'                 => [
 | 
			
		||||
                    'fee_payer' => config('ninja.wepay.fee_payer'),
 | 
			
		||||
                    'app_fee' => $app_fee,
 | 
			
		||||
                ],
 | 
			
		||||
                'payment_method'      => [
 | 
			
		||||
                    'type'            => 'credit_card',
 | 
			
		||||
                    'credit_card'     => [
 | 
			
		||||
                        'id'          => $credit_card_id,
 | 
			
		||||
                    ],
 | 
			
		||||
                ],
 | 
			
		||||
            ]);
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            $this->wepay_payment_driver->sendFailureMail($e->getMessage());
 | 
			
		||||
 | 
			
		||||
            $message = [
 | 
			
		||||
                'server_response' => $e->getMessage(),
 | 
			
		||||
                'data' => $this->wepay_payment_driver->payment_hash->data,
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
            SystemLogger::dispatch(
 | 
			
		||||
                $e->getMessage(),
 | 
			
		||||
                SystemLog::CATEGORY_GATEWAY_RESPONSE,
 | 
			
		||||
                SystemLog::EVENT_GATEWAY_FAILURE,
 | 
			
		||||
                SystemLog::TYPE_WEPAY,
 | 
			
		||||
                $this->wepay_payment_driver->client,
 | 
			
		||||
                $this->wepay_payment_driver->client->company,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            throw new PaymentFailed($e->getMessage(), 500);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Merge all data and store in the payment hash*/
 | 
			
		||||
        $state = [
 | 
			
		||||
            'server_response' => $response,
 | 
			
		||||
            'payment_hash' => $request->payment_hash,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $state = array_merge($state, $request->all());
 | 
			
		||||
        $this->wepay_payment_driver->payment_hash->data = array_merge((array) $this->wepay_payment_driver->payment_hash->data, $state);
 | 
			
		||||
        $this->wepay_payment_driver->payment_hash->save();
 | 
			
		||||
 | 
			
		||||
        if (in_array($response->state, ['authorized', 'captured'])) {
 | 
			
		||||
            //success
 | 
			
		||||
            nlog('success');
 | 
			
		||||
            $payment_status = $response->state == 'authorized' ? Payment::STATUS_COMPLETED : Payment::STATUS_PENDING;
 | 
			
		||||
 | 
			
		||||
            return $this->processSuccessfulPayment($response, $payment_status, GatewayType::CREDIT_CARD);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (in_array($response->state, ['released', 'cancelled', 'failed', 'expired'])) {
 | 
			
		||||
            //some type of failure
 | 
			
		||||
            nlog('failure');
 | 
			
		||||
 | 
			
		||||
            $payment_status = $response->state == 'cancelled' ? Payment::STATUS_CANCELLED : Payment::STATUS_FAILED;
 | 
			
		||||
 | 
			
		||||
            $this->processUnSuccessfulPayment($response, $payment_status);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    new The checkout was created by the application. This state typically indicates that checkouts created in WePay's hosted checkout flow are waiting for the payer to submit their information.
 | 
			
		||||
    authorized  The payer entered their payment info and confirmed the payment on WePay. WePay has successfully charged the card.
 | 
			
		||||
    captured    The payment has been reserved from the payer.
 | 
			
		||||
    released    The payment has been credited to the payee account. Note that the released state may be active although there are active partial refunds or partial chargebacks.
 | 
			
		||||
    cancelled   The payment has been cancelled by the payer, payee, or application.
 | 
			
		||||
    refunded    The payment was captured and then refunded by the payer, payee, or application. The payment has been debited from the payee account.
 | 
			
		||||
    charged back    The payment has been charged back by the payer and the payment has been debited from the payee account.
 | 
			
		||||
    failed  The payment has failed.
 | 
			
		||||
    expired Checkouts expire if they remain in the new state for more than 30 minutes (e.g., they have been abandoned).
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    https://developer.wepay.com/api/api-calls/checkout
 | 
			
		||||
    {
 | 
			
		||||
        "checkout_id": 649945633,
 | 
			
		||||
        "account_id": 1548718026,
 | 
			
		||||
        "type": "donation",
 | 
			
		||||
        "short_description": "test checkout",
 | 
			
		||||
        "currency": "USD",
 | 
			
		||||
        "amount": 20,
 | 
			
		||||
        "state": "authorized",
 | 
			
		||||
        "soft_descriptor": "WPY*Wolverine",
 | 
			
		||||
        "auto_release": true,
 | 
			
		||||
        "create_time": 1463589958,
 | 
			
		||||
        "gross": 20.88,
 | 
			
		||||
        "reference_id": null,
 | 
			
		||||
        "callback_uri": null,
 | 
			
		||||
        "long_description": null,
 | 
			
		||||
        "delivery_type": null,
 | 
			
		||||
        "initiated_by": "merchant",
 | 
			
		||||
        "in_review": false,
 | 
			
		||||
        "fee": {
 | 
			
		||||
            "app_fee": 0,
 | 
			
		||||
            "processing_fee": 0.88,
 | 
			
		||||
            "fee_payer": "payer"
 | 
			
		||||
        },
 | 
			
		||||
        "chargeback": {
 | 
			
		||||
            "amount_charged_back": 0,
 | 
			
		||||
            "dispute_uri": null
 | 
			
		||||
        },
 | 
			
		||||
        "refund": {
 | 
			
		||||
            "amount_refunded": 0,
 | 
			
		||||
            "refund_reason": null
 | 
			
		||||
        },
 | 
			
		||||
        "payment_method": {
 | 
			
		||||
            "type": "credit_card",
 | 
			
		||||
            "credit_card": {
 | 
			
		||||
                "id": 1684847614,
 | 
			
		||||
                "data": {
 | 
			
		||||
                    "emv_receipt": null,
 | 
			
		||||
                    "signature_url": null
 | 
			
		||||
                },
 | 
			
		||||
                "auto_release": false
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "hosted_checkout": null,
 | 
			
		||||
        "payer": {
 | 
			
		||||
            "email": "test@example.com",
 | 
			
		||||
            "name": "Mr Smith",
 | 
			
		||||
            "home_address": null
 | 
			
		||||
        },
 | 
			
		||||
        "npo_information": null,
 | 
			
		||||
        "payment_error": null
 | 
			
		||||
    }
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    private function storePaymentMethod($response, $payment_method_id)
 | 
			
		||||
    {
 | 
			
		||||
        nlog('storing card');
 | 
			
		||||
 | 
			
		||||
        $payment_meta = new \stdClass();
 | 
			
		||||
        $payment_meta->exp_month = (string) $response->expiration_month;
 | 
			
		||||
        $payment_meta->exp_year = (string) $response->expiration_year;
 | 
			
		||||
        $payment_meta->brand = (string) $response->credit_card_name;
 | 
			
		||||
        $payment_meta->last4 = (string) $response->last_four;
 | 
			
		||||
        $payment_meta->type = GatewayType::CREDIT_CARD;
 | 
			
		||||
 | 
			
		||||
        $data = [
 | 
			
		||||
            'payment_meta' => $payment_meta,
 | 
			
		||||
            'token' => $response->credit_card_id,
 | 
			
		||||
            'payment_method_id' => $payment_method_id,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $this->wepay_payment_driver->storeGatewayToken($data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function tokenBilling($cgt, $payment_hash)
 | 
			
		||||
    {
 | 
			
		||||
        $amount = array_sum(array_column($this->wepay_payment_driver->payment_hash->invoices(), 'amount')) + $this->wepay_payment_driver->payment_hash->fee_total;
 | 
			
		||||
 | 
			
		||||
        $app_fee = (config('ninja.wepay.fee_cc_multiplier') * $amount) + config('ninja.wepay.fee_fixed');
 | 
			
		||||
        // charge the credit card
 | 
			
		||||
        $response = $this->wepay_payment_driver->wepay->request('checkout/create', [
 | 
			
		||||
            'unique_id'           => Str::random(40),
 | 
			
		||||
            'account_id'          => $this->wepay_payment_driver->company_gateway->getConfigField('accountId'),
 | 
			
		||||
            'amount'              => $amount,
 | 
			
		||||
            'currency'            => $this->wepay_payment_driver->client->getCurrencyCode(),
 | 
			
		||||
            'short_description'   => 'Goods and services',
 | 
			
		||||
            'type'                => 'goods',
 | 
			
		||||
            'fee'                 => [
 | 
			
		||||
                'fee_payer' => config('ninja.wepay.fee_payer'),
 | 
			
		||||
                'app_fee' => $app_fee,
 | 
			
		||||
            ],
 | 
			
		||||
            'payment_method'      => [
 | 
			
		||||
                'type'            => 'credit_card',
 | 
			
		||||
                'credit_card'     => [
 | 
			
		||||
                    'id'          => $cgt->token,
 | 
			
		||||
                ],
 | 
			
		||||
            ],
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        /* Merge all data and store in the payment hash*/
 | 
			
		||||
        $state = [
 | 
			
		||||
            'server_response' => $response,
 | 
			
		||||
            'payment_hash' => $payment_hash,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $this->wepay_payment_driver->payment_hash->data = array_merge((array) $this->wepay_payment_driver->payment_hash->data, $state);
 | 
			
		||||
        $this->wepay_payment_driver->payment_hash->save();
 | 
			
		||||
 | 
			
		||||
        if (in_array($response->state, ['authorized', 'captured'])) {
 | 
			
		||||
            //success
 | 
			
		||||
            nlog('success');
 | 
			
		||||
            $payment_status = $response->state == 'authorized' ? Payment::STATUS_COMPLETED : Payment::STATUS_PENDING;
 | 
			
		||||
 | 
			
		||||
            return $this->processSuccessfulPayment($response, $payment_status, GatewayType::CREDIT_CARD, true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (in_array($response->state, ['released', 'cancelled', 'failed', 'expired'])) {
 | 
			
		||||
            //some type of failure
 | 
			
		||||
            nlog('failure');
 | 
			
		||||
 | 
			
		||||
            $payment_status = $response->state == 'cancelled' ? Payment::STATUS_CANCELLED : Payment::STATUS_FAILED;
 | 
			
		||||
 | 
			
		||||
            $this->processUnSuccessfulPayment($response, $payment_status);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,34 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
 *
 | 
			
		||||
 * @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
 *
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\PaymentDrivers\WePay;
 | 
			
		||||
 | 
			
		||||
use App\PaymentDrivers\WePayPaymentDriver;
 | 
			
		||||
 | 
			
		||||
class Setup
 | 
			
		||||
{
 | 
			
		||||
    public $wepay;
 | 
			
		||||
 | 
			
		||||
    public function __construct(WePayPaymentDriver $wepay)
 | 
			
		||||
    {
 | 
			
		||||
        $this->wepay = $wepay;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function boot($data)
 | 
			
		||||
    {
 | 
			
		||||
        /*
 | 
			
		||||
        'user_id',
 | 
			
		||||
        'user_company',
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        return render('gateways.wepay.signup.index', $data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,75 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
 *
 | 
			
		||||
 * @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
 *
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\PaymentDrivers\WePay;
 | 
			
		||||
 | 
			
		||||
use App\Exceptions\PaymentFailed;
 | 
			
		||||
use App\Jobs\Util\SystemLogger;
 | 
			
		||||
use App\Models\GatewayType;
 | 
			
		||||
use App\Models\PaymentType;
 | 
			
		||||
use App\Models\SystemLog;
 | 
			
		||||
 | 
			
		||||
trait WePayCommon
 | 
			
		||||
{
 | 
			
		||||
    private function processSuccessfulPayment($response, $payment_status, $gateway_type, $return_payment = false)
 | 
			
		||||
    {
 | 
			
		||||
        if ($gateway_type == GatewayType::BANK_TRANSFER) {
 | 
			
		||||
            $payment_type = PaymentType::ACH;
 | 
			
		||||
        } else {
 | 
			
		||||
            $payment_type = PaymentType::CREDIT_CARD_OTHER;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $data = [
 | 
			
		||||
            'payment_type' => $payment_type,
 | 
			
		||||
            'amount' => $response->amount,
 | 
			
		||||
            'transaction_reference' => $response->checkout_id,
 | 
			
		||||
            'gateway_type_id' => $gateway_type,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $payment = $this->wepay_payment_driver->createPayment($data, $payment_status);
 | 
			
		||||
 | 
			
		||||
        SystemLogger::dispatch(
 | 
			
		||||
            ['response' => $this->wepay_payment_driver->payment_hash->data->server_response, 'data' => $data],
 | 
			
		||||
            SystemLog::CATEGORY_GATEWAY_RESPONSE,
 | 
			
		||||
            SystemLog::EVENT_GATEWAY_SUCCESS,
 | 
			
		||||
            SystemLog::TYPE_WEPAY,
 | 
			
		||||
            $this->wepay_payment_driver->client,
 | 
			
		||||
            $this->wepay_payment_driver->client->company,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if ($return_payment) {
 | 
			
		||||
            return $payment;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return redirect()->route('client.payments.show', ['payment' => $this->wepay_payment_driver->encodePrimaryKey($payment->id)]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function processUnSuccessfulPayment($response, $payment_status)
 | 
			
		||||
    {
 | 
			
		||||
        $this->wepay_payment_driver->sendFailureMail($response->state);
 | 
			
		||||
 | 
			
		||||
        $message = [
 | 
			
		||||
            'server_response' => $response,
 | 
			
		||||
            'data' => $this->wepay_payment_driver->payment_hash->data,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        SystemLogger::dispatch(
 | 
			
		||||
            $message,
 | 
			
		||||
            SystemLog::CATEGORY_GATEWAY_RESPONSE,
 | 
			
		||||
            SystemLog::EVENT_GATEWAY_FAILURE,
 | 
			
		||||
            SystemLog::TYPE_WEPAY,
 | 
			
		||||
            $this->wepay_payment_driver->client,
 | 
			
		||||
            $this->wepay_payment_driver->client->company,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        throw new PaymentFailed('Failed to process the payment.', 500);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -17,13 +17,12 @@ use App\Models\GatewayType;
 | 
			
		||||
use App\Models\Payment;
 | 
			
		||||
use App\Models\PaymentHash;
 | 
			
		||||
use App\Models\SystemLog;
 | 
			
		||||
use App\PaymentDrivers\WePay\ACH;
 | 
			
		||||
use App\PaymentDrivers\WePay\CreditCard;
 | 
			
		||||
use App\PaymentDrivers\WePay\Setup;
 | 
			
		||||
use App\Utils\Traits\MakesHash;
 | 
			
		||||
use Illuminate\Http\Request;
 | 
			
		||||
use WePay;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @deprecated 5.9
 | 
			
		||||
 */
 | 
			
		||||
class WePayPaymentDriver extends BaseDriver
 | 
			
		||||
{
 | 
			
		||||
    use MakesHash;
 | 
			
		||||
@ -45,27 +44,14 @@ class WePayPaymentDriver extends BaseDriver
 | 
			
		||||
 | 
			
		||||
    /* Maps the Payment Gateway Type - to its implementation */
 | 
			
		||||
    public static $methods = [
 | 
			
		||||
        GatewayType::CREDIT_CARD => CreditCard::class,
 | 
			
		||||
        GatewayType::BANK_TRANSFER => ACH::class,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    public const SYSTEM_LOG_TYPE = SystemLog::TYPE_WEPAY;
 | 
			
		||||
 | 
			
		||||
    public function init()
 | 
			
		||||
    {
 | 
			
		||||
        if (WePay::getEnvironment() == 'none') {
 | 
			
		||||
            if (config('ninja.wepay.environment') == 'staging') {
 | 
			
		||||
                WePay::useStaging(config('ninja.wepay.client_id'), config('ninja.wepay.client_secret'));
 | 
			
		||||
            } else {
 | 
			
		||||
                WePay::useProduction(config('ninja.wepay.client_id'), config('ninja.wepay.client_secret'));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        throw new \Exception("Gateway no longer supported", 500);
 | 
			
		||||
 | 
			
		||||
        if ($this->company_gateway) {
 | 
			
		||||
            $this->wepay = new WePay($this->company_gateway->getConfigField('accessToken'));
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->wepay = new WePay(null);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
@ -93,7 +79,6 @@ class WePayPaymentDriver extends BaseDriver
 | 
			
		||||
     */
 | 
			
		||||
    public function setup(array $data)
 | 
			
		||||
    {
 | 
			
		||||
        return (new Setup($this))->boot($data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -168,140 +153,17 @@ class WePayPaymentDriver extends BaseDriver
 | 
			
		||||
    public function processWebhookRequest(PaymentWebhookRequest $request, Payment $payment = null)
 | 
			
		||||
    {
 | 
			
		||||
        $this->init();
 | 
			
		||||
 | 
			
		||||
        $input = $request->all();
 | 
			
		||||
 | 
			
		||||
        $config = $this->company_gateway->getConfig();
 | 
			
		||||
 | 
			
		||||
        $accountId = $this->company_gateway->getConfigField('accountId');
 | 
			
		||||
        $objectId = false;
 | 
			
		||||
        $objectType = '';
 | 
			
		||||
 | 
			
		||||
        foreach (array_keys($input) as $key) {
 | 
			
		||||
            if ('_id' == substr($key, -3)) {
 | 
			
		||||
                $objectType = substr($key, 0, -3);
 | 
			
		||||
                $objectId = $input[$key];
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (! $objectId) {
 | 
			
		||||
            throw new \Exception('Could not find object id parameter');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($objectType == 'credit_card') {
 | 
			
		||||
            $payment_method = ClientGatewayToken::where('token', $objectId)->first();
 | 
			
		||||
 | 
			
		||||
            if (! $payment_method) {
 | 
			
		||||
                throw new \Exception('Unknown payment method');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $source = $this->wepay->request('credit_card', [
 | 
			
		||||
                'client_id'          => config('ninja.wepay.client_id'),
 | 
			
		||||
                'client_secret'      => config('ninja.wepay.client_secret'),
 | 
			
		||||
                'credit_card_id'     => (int) $objectId,
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
            if ($source->state == 'deleted') {
 | 
			
		||||
                $payment_method->delete();
 | 
			
		||||
            } else {
 | 
			
		||||
                //$this->paymentService->convertPaymentMethodFromWePay($source, null, $paymentMethod)->save();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return 'Processed successfully';
 | 
			
		||||
        } elseif ($objectType == 'account') {
 | 
			
		||||
            if ($accountId != $objectId) {
 | 
			
		||||
                throw new \Exception('Unknown account '.$accountId.' does not equal '.$objectId);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $wepayAccount = $this->wepay->request('account', [
 | 
			
		||||
                'account_id'     => (int) $objectId,
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
            if ($wepayAccount->state == 'deleted') {
 | 
			
		||||
                $this->company_gateway->delete();
 | 
			
		||||
            } else {
 | 
			
		||||
                $config->state = $wepayAccount->state;
 | 
			
		||||
                $this->company_gateway->setConfig($config);
 | 
			
		||||
                $this->company_gateway->save();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return ['message' => 'Processed successfully'];
 | 
			
		||||
        } elseif ($objectType == 'checkout') {
 | 
			
		||||
            /** @var \App\Models\Payment $payment */
 | 
			
		||||
            $payment = Payment::where('company_id', $this->company_gateway->company_id)
 | 
			
		||||
                              ->where('transaction_reference', '=', $objectId)
 | 
			
		||||
                              ->first();
 | 
			
		||||
 | 
			
		||||
            if (! $payment) {
 | 
			
		||||
                throw new \Exception('Unknown payment');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($payment->is_deleted) {
 | 
			
		||||
                throw new \Exception('Payment is deleted');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $checkout = $this->wepay->request('checkout', [
 | 
			
		||||
                'checkout_id' => intval($objectId),
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
            if ($checkout->state == 'captured') {
 | 
			
		||||
                $payment->status_id = Payment::STATUS_COMPLETED;
 | 
			
		||||
                $payment->save();
 | 
			
		||||
            } elseif ($checkout->state == 'cancelled') {
 | 
			
		||||
                $payment->service()->deletePayment()->save();
 | 
			
		||||
            } elseif ($checkout->state == 'failed') {
 | 
			
		||||
                $payment->status_id = Payment::STATUS_FAILED;
 | 
			
		||||
                $payment->save();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return 'Processed successfully';
 | 
			
		||||
        } else {
 | 
			
		||||
            return 'Ignoring event';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function refund(Payment $payment, $amount, $return_client_response = false)
 | 
			
		||||
    {
 | 
			
		||||
        $this->init();
 | 
			
		||||
 | 
			
		||||
        $response = $this->wepay->request('checkout/refund', [
 | 
			
		||||
            'checkout_id'   => $payment->transaction_reference,
 | 
			
		||||
            'refund_reason' => 'Refund by merchant',
 | 
			
		||||
            'amount'        => $amount,
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
            'transaction_reference' => $response->checkout_id,
 | 
			
		||||
            'transaction_response' => json_encode($response),
 | 
			
		||||
            'success' => $response->state == 'refunded' ? true : false,
 | 
			
		||||
            'description' => 'refund',
 | 
			
		||||
            'code' => 0,
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function detach(ClientGatewayToken $token)
 | 
			
		||||
    {
 | 
			
		||||
        /*Bank accounts cannot be deleted - only CC*/
 | 
			
		||||
        if ($token->gateway_type_id == 2) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    
 | 
			
		||||
        $this->init();
 | 
			
		||||
 | 
			
		||||
        $response = $this->wepay->request('/credit_card/delete', [
 | 
			
		||||
            'client_id'          => config('ninja.wepay.client_id'),
 | 
			
		||||
            'client_secret'      => config('ninja.wepay.client_secret'),
 | 
			
		||||
            'credit_card_id'     => intval($token->token),
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        if ($response->state == 'deleted') {
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new \Exception(trans('texts.failed_remove_payment_method'));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getClientRequiredFields(): array
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,13 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
 *
 | 
			
		||||
 * @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
 *
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Providers;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,6 @@ class BankTransactionRepository extends BaseRepository
 | 
			
		||||
            $bank_transaction->bank_integration_id = $data['bank_integration_id'];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $bank_transaction->fill($data);
 | 
			
		||||
        $bank_transaction->save();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										288
									
								
								app/Services/EDocument/Standards/FatturaPANew.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										288
									
								
								app/Services/EDocument/Standards/FatturaPANew.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,288 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * Invoice Ninja (https://invoiceninja.com).
 | 
			
		||||
 *
 | 
			
		||||
 * @link https://github.com/invoiceninja/invoiceninja source repository
 | 
			
		||||
 *
 | 
			
		||||
 * @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
 *
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Services\EDocument\Standards;
 | 
			
		||||
 | 
			
		||||
use App\Models\Invoice;
 | 
			
		||||
use App\Services\AbstractService;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\FatturaElettronica;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\IndirizzoType\Sede;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\AnagraficaType\Anagrafica;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\IdFiscaleType\IdFiscaleIVA;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\IdFiscaleType\IdTrasmittente;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\DatiGeneraliType\DatiGenerali;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\DatiPagamentoType\DatiPagamento;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\DatiRiepilogoType\DatiRiepilogo;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\DettaglioLineeType\DettaglioLinee;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\DatiBeniServiziType\DatiBeniServizi;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\DatiTrasmissioneType\DatiTrasmissione;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\CedentePrestatoreType\CedentePrestatore;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\DatiAnagraficiCedenteType\DatiAnagrafici;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\DettaglioPagamentoType\DettaglioPagamento;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\DatiGeneraliDocumentoType\DatiGeneraliDocumento;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\CessionarioCommittenteType\CessionarioCommittente;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\FatturaElettronicaBodyType\FatturaElettronicaBody;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\FatturaElettronicaHeaderType\FatturaElettronicaHeader;
 | 
			
		||||
 | 
			
		||||
class FatturaPANew extends AbstractService
 | 
			
		||||
{
 | 
			
		||||
    private FatturaElettronica $FatturaElettronica;
 | 
			
		||||
    private FatturaElettronicaBody $FatturaElettronicaBody;
 | 
			
		||||
    private FatturaElettronicaHeader $FatturaElettronicaHeader;
 | 
			
		||||
    private DatiTrasmissione $DatiTrasmissione;
 | 
			
		||||
    private IdTrasmittente $IdTrasmittente;
 | 
			
		||||
    private CedentePrestatore $CedentePrestatore;
 | 
			
		||||
    private DatiAnagrafici $DatiAnagrafici;
 | 
			
		||||
    private IdFiscaleIVA $IdFiscaleIVA;
 | 
			
		||||
    private Anagrafica $Anagrafica;
 | 
			
		||||
    private DatiGeneraliDocumento $DatiGeneraliDocumento;
 | 
			
		||||
    private DatiGenerali $DatiGenerali;
 | 
			
		||||
    private DettaglioPagamento $DettaglioPagamento;
 | 
			
		||||
    /**
 | 
			
		||||
     * @param Invoice $invoice
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct(public Invoice $invoice)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function run()
 | 
			
		||||
    {
 | 
			
		||||
        $this->init()
 | 
			
		||||
             ->setIdTrasmittente() //order of execution matters.
 | 
			
		||||
             ->setDatiTrasmissione()
 | 
			
		||||
             ->setIdFiscaleIVA()
 | 
			
		||||
             ->setAnagrafica()
 | 
			
		||||
             ->setDatiAnagrafici()
 | 
			
		||||
             ->setCedentePrestatore()
 | 
			
		||||
             ->setClientDetails()
 | 
			
		||||
             ->setDatiGeneraliDocumento()
 | 
			
		||||
             ->setDatiGenerali()
 | 
			
		||||
             ->setLineItems()
 | 
			
		||||
             ->setDettaglioPagamento()
 | 
			
		||||
             ->setFatturaElettronica();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getFatturaElettronica(): FatturaElettronica
 | 
			
		||||
    {
 | 
			
		||||
        return $this->FatturaElettronica;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function setDatiTrasmissione(): self
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $this->DatiTrasmissione->FormatoTrasmissione = "FPR12";
 | 
			
		||||
        $this->DatiTrasmissione->CodiceDestinatario = $this->invoice->client->routing_id;
 | 
			
		||||
        $this->DatiTrasmissione->ProgressivoInvio = $this->invoice->number;
 | 
			
		||||
 | 
			
		||||
        $this->DatiTrasmissione->IdTrasmittente = $this->IdTrasmittente;
 | 
			
		||||
 | 
			
		||||
        $this->FatturaElettronicaHeader->DatiTrasmissione = $this->DatiTrasmissione;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private function setIdTrasmittente():self
 | 
			
		||||
    {
 | 
			
		||||
        $this->IdTrasmittente->IdPaese = $this->invoice->company->country()->iso_3166_2;
 | 
			
		||||
        $this->IdTrasmittente->IdCodice = $this->invoice->company->settings->vat_number;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function setCedentePrestatore():self
 | 
			
		||||
    {
 | 
			
		||||
        $this->CedentePrestatore->DatiAnagrafici = $this->DatiAnagrafici;
 | 
			
		||||
 | 
			
		||||
        $sede = new Sede;
 | 
			
		||||
        $sede->Indirizzo = $this->invoice->company->settings->address1;
 | 
			
		||||
        $sede->CAP = (int)$this->invoice->company->settings->postal_code;
 | 
			
		||||
        $sede->Comune = $this->invoice->company->settings->city;
 | 
			
		||||
        $sede->Provincia = $this->invoice->company->settings->state;
 | 
			
		||||
        $sede->Nazione = $this->invoice->company->country()->iso_3166_2;
 | 
			
		||||
        $this->CedentePrestatore->Sede = $sede;
 | 
			
		||||
 | 
			
		||||
        $this->FatturaElettronicaHeader->CedentePrestatore = $this->CedentePrestatore;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function setDatiAnagrafici():self
 | 
			
		||||
    {
 | 
			
		||||
        $this->DatiAnagrafici->RegimeFiscale = "RF01";
 | 
			
		||||
        $this->DatiAnagrafici->Anagrafica = $this->Anagrafica;
 | 
			
		||||
        $this->DatiAnagrafici->IdFiscaleIVA = $this->IdFiscaleIVA;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function setClientDetails():self
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $datiAnagrafici = new DatiAnagrafici();
 | 
			
		||||
        $anagrafica = new Anagrafica();
 | 
			
		||||
        $anagrafica->Denominazione =  $this->invoice->client->present()->name();
 | 
			
		||||
        $datiAnagrafici->Anagrafica = $anagrafica;
 | 
			
		||||
 | 
			
		||||
        $idFiscale = new IdFiscaleIVA;
 | 
			
		||||
        $idFiscale->IdCodice= $this->invoice->client->vat_number;
 | 
			
		||||
        $idFiscale->IdPaese = $this->invoice->client->country->iso_3166_2;
 | 
			
		||||
 | 
			
		||||
        $datiAnagrafici->IdFiscaleIVA = $idFiscale;
 | 
			
		||||
        
 | 
			
		||||
        $sede = new Sede;
 | 
			
		||||
        $sede->Indirizzo =  $this->invoice->client->address1;
 | 
			
		||||
        $sede->CAP =  (int)$this->invoice->client->postal_code;
 | 
			
		||||
        $sede->Comune =  $this->invoice->client->city;
 | 
			
		||||
        $sede->Provincia =  $this->invoice->client->state;
 | 
			
		||||
        $sede->Nazione = $this->invoice->client->country->iso_3166_2;
 | 
			
		||||
 | 
			
		||||
        $cessionarioCommittente = new CessionarioCommittente;
 | 
			
		||||
        $cessionarioCommittente->DatiAnagrafici = $datiAnagrafici;
 | 
			
		||||
        $cessionarioCommittente->Sede = $sede;
 | 
			
		||||
 | 
			
		||||
        $this->FatturaElettronicaHeader->CessionarioCommittente = $cessionarioCommittente;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function setIdFiscaleIVA():self
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $this->IdFiscaleIVA->IdPaese = $this->invoice->company->country()->iso_3166_2;
 | 
			
		||||
        $this->IdFiscaleIVA->IdCodice = $this->invoice->company->settings->vat_number;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //this is a choice, need to switch based on values here.
 | 
			
		||||
    private function setAnagrafica():self
 | 
			
		||||
    {
 | 
			
		||||
        $this->Anagrafica->Denominazione = $this->invoice->company->present()->name();
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function setDatiGeneraliDocumento():self
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $this->DatiGeneraliDocumento->TipoDocumento = "TD01";
 | 
			
		||||
        $this->DatiGeneraliDocumento->Divisa = $this->invoice->client->currency()->code;
 | 
			
		||||
        $this->DatiGeneraliDocumento->Data = new \DateTime($this->invoice->date);
 | 
			
		||||
        $this->DatiGeneraliDocumento->Numero = $this->invoice->number;
 | 
			
		||||
        $this->DatiGeneraliDocumento->Causale[] = substr($this->invoice->public_notes ?? '',0, 200); //unsure..
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function setDatiGenerali():self
 | 
			
		||||
    {
 | 
			
		||||
        $this->DatiGenerali->DatiGeneraliDocumento = $this->DatiGeneraliDocumento;
 | 
			
		||||
 | 
			
		||||
        $this->FatturaElettronicaBody->DatiGenerali = $this->DatiGenerali;
 | 
			
		||||
        
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function setDettaglioPagamento():self
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $this->DettaglioPagamento->ModalitaPagamento =  "MP01"; //String
 | 
			
		||||
        $this->DettaglioPagamento->DataScadenzaPagamento =  new \DateTime($this->invoice->due_date ?? $this->invoice->date);
 | 
			
		||||
        $this->DettaglioPagamento->ImportoPagamento =  (string) sprintf('%0.2f', $this->invoice->balance);
 | 
			
		||||
 | 
			
		||||
        $DatiPagamento = new DatiPagamento;
 | 
			
		||||
        $DatiPagamento->CondizioniPagamento = "TP02";
 | 
			
		||||
        $DatiPagamento->DettaglioPagamento[] = $this->DettaglioPagamento;
 | 
			
		||||
 | 
			
		||||
        $this->FatturaElettronicaBody->DatiPagamento[] = $DatiPagamento;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function setLineItems(): self
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $calc = $this->invoice->calc();
 | 
			
		||||
 | 
			
		||||
        $datiBeniServizi  = new DatiBeniServizi();
 | 
			
		||||
        $tax_rate_level = 0;
 | 
			
		||||
        //line items
 | 
			
		||||
        foreach ($this->invoice->line_items as $key => $item) {
 | 
			
		||||
 | 
			
		||||
            $numero = $key + 1;
 | 
			
		||||
            $dettaglioLinee = new DettaglioLinee;
 | 
			
		||||
            $dettaglioLinee->NumeroLinea =  "{$numero}";
 | 
			
		||||
            $dettaglioLinee->Descrizione =  $item->notes ?? 'Descrizione';
 | 
			
		||||
            $dettaglioLinee->Quantita =  sprintf('%0.2f', $item->quantity);
 | 
			
		||||
            $dettaglioLinee->PrezzoUnitario =  sprintf('%0.2f', $item->cost);
 | 
			
		||||
            $dettaglioLinee->PrezzoTotale =  sprintf('%0.2f', $item->line_total);
 | 
			
		||||
            $dettaglioLinee->AliquotaIVA =  sprintf('%0.2f', $item->tax_rate1);
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            $datiBeniServizi->DettaglioLinee[] = $dettaglioLinee;
 | 
			
		||||
 | 
			
		||||
            if ($item->tax_rate1 > $tax_rate_level) {
 | 
			
		||||
                $tax_rate_level = sprintf('%0.2f', $item->tax_rate1);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //totals
 | 
			
		||||
        if($this->invoice->tax_rate1 > $tax_rate_level) {
 | 
			
		||||
            $tax_rate_level = sprintf('%0.2f', $this->invoice->tax_rate1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $subtotal = sprintf('%0.2f', $calc->getSubTotal());
 | 
			
		||||
        $taxes = sprintf('%0.2f', $calc->getTotalTaxes());
 | 
			
		||||
 | 
			
		||||
        $datiRiepilogo = new DatiRiepilogo;
 | 
			
		||||
        $datiRiepilogo->AliquotaIVA = "{$tax_rate_level}";
 | 
			
		||||
        $datiRiepilogo->ImponibileImporto = "{$subtotal}";
 | 
			
		||||
        $datiRiepilogo->Imposta = "{$taxes}";
 | 
			
		||||
        $datiRiepilogo->EsigibilitaIVA = "I";
 | 
			
		||||
 | 
			
		||||
        $datiBeniServizi->DatiRiepilogo[] = $datiRiepilogo;
 | 
			
		||||
 | 
			
		||||
        $this->FatturaElettronicaBody->DatiBeniServizi = $datiBeniServizi;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function setFatturaElettronica(): self
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $this->FatturaElettronica->FatturaElettronicaBody[] = $this->FatturaElettronicaBody;
 | 
			
		||||
        $this->FatturaElettronica->FatturaElettronicaHeader = $this->FatturaElettronicaHeader;
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function init(): self
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $this->FatturaElettronica = new FatturaElettronica;
 | 
			
		||||
        $this->FatturaElettronicaBody = new FatturaElettronicaBody;
 | 
			
		||||
        $this->FatturaElettronicaHeader = new FatturaElettronicaHeader;
 | 
			
		||||
        $this->DatiTrasmissione = new DatiTrasmissione;
 | 
			
		||||
        $this->IdTrasmittente = new IdTrasmittente;
 | 
			
		||||
        $this->CedentePrestatore = new CedentePrestatore;
 | 
			
		||||
        $this->DatiAnagrafici = new DatiAnagrafici;
 | 
			
		||||
        $this->IdFiscaleIVA = new IdFiscaleIVA;
 | 
			
		||||
        $this->Anagrafica = new Anagrafica;
 | 
			
		||||
        $this->DatiGeneraliDocumento = new DatiGeneraliDocumento;
 | 
			
		||||
        $this->DatiGenerali = new DatiGenerali;
 | 
			
		||||
        $this->DettaglioPagamento = new DettaglioPagamento;
 | 
			
		||||
        
 | 
			
		||||
        return $this;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -302,7 +302,26 @@ class Email implements ShouldQueue
 | 
			
		||||
            $this->cleanUpMailers();
 | 
			
		||||
            $this->logMailError($e->getMessage(), $this->company->clients()->first());
 | 
			
		||||
            return;
 | 
			
		||||
        } catch (\Exception | \RuntimeException | \Google\Service\Exception $e) {
 | 
			
		||||
        }
 | 
			
		||||
        catch(\Symfony\Component\Mailer\Transport\Dsn $e){
 | 
			
		||||
            nlog("Incorrectly configured mail server - setting to default mail driver.");
 | 
			
		||||
            $this->email_object->settings->email_sending_method = 'default';
 | 
			
		||||
            return $this->setMailDriver();
 | 
			
		||||
        }
 | 
			
		||||
        catch(\Google\Service\Exception $e){
 | 
			
		||||
 | 
			
		||||
            if ($e->getCode() == '429') {
 | 
			
		||||
            
 | 
			
		||||
                $message = "Google rate limiting triggered, we are queueing based on Gmail requirements.";
 | 
			
		||||
                $this->logMailError($message, $this->company->clients()->first());
 | 
			
		||||
                sleep(rand(1, 2));
 | 
			
		||||
                $this->release(900);
 | 
			
		||||
                $message = null;
 | 
			
		||||
            }
 | 
			
		||||
        
 | 
			
		||||
        } 
 | 
			
		||||
        
 | 
			
		||||
        catch (\Exception | \RuntimeException $e) {
 | 
			
		||||
            nlog("Mailer failed with {$e->getMessage()}");
 | 
			
		||||
            $message = $e->getMessage();
 | 
			
		||||
 | 
			
		||||
@ -916,6 +935,7 @@ class Email implements ShouldQueue
 | 
			
		||||
            $guzzle = new \GuzzleHttp\Client();
 | 
			
		||||
            $url = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                $token = json_decode($guzzle->post($url, [
 | 
			
		||||
                    'form_params' => [
 | 
			
		||||
                        'client_id' => config('ninja.o365.client_id'),
 | 
			
		||||
@ -925,6 +945,10 @@ class Email implements ShouldQueue
 | 
			
		||||
                        'refresh_token' => $user->oauth_user_refresh_token
 | 
			
		||||
                    ],
 | 
			
		||||
                ])->getBody()->getContents());
 | 
			
		||||
            }
 | 
			
		||||
            catch(\Exception $e){
 | 
			
		||||
                nlog("Problem getting new Microsoft token for User: {$user->email}");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($token) {
 | 
			
		||||
                $user->oauth_user_refresh_token = property_exists($token, 'refresh_token') ? $token->refresh_token : $user->oauth_user_refresh_token;
 | 
			
		||||
 | 
			
		||||
@ -166,9 +166,9 @@ class EmailDefaults
 | 
			
		||||
    private function setBody(): self
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if (strlen($this->email->email_object->body) > 3) {
 | 
			
		||||
        if (strlen($this->email->email_object->body ?? '') > 3) {
 | 
			
		||||
            // A Custom Message has been set in the email screen.
 | 
			
		||||
        } elseif (strlen($this->email->email_object->settings?->{$this->email->email_object->email_template_body}) > 3) {
 | 
			
		||||
        } elseif (strlen($this->email->email_object->settings?->{$this->email->email_object->email_template_body} ?? '') > 3) {
 | 
			
		||||
            // A body has been saved in the settings.
 | 
			
		||||
            $this->email->email_object->body = $this->email->email_object->settings?->{$this->email->email_object->email_template_body};
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
@ -59,6 +59,7 @@ class DeletePayment
 | 
			
		||||
        $this->payment->delete();
 | 
			
		||||
 | 
			
		||||
        BankTransaction::query()->where('payment_id', $this->payment->id)->cursor()->each(function ($bt) {
 | 
			
		||||
            $bt->invoice_ids = null;
 | 
			
		||||
            $bt->payment_id = null;
 | 
			
		||||
            $bt->status_id = 1;
 | 
			
		||||
            $bt->save();
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@
 | 
			
		||||
    ],
 | 
			
		||||
    "type": "project",
 | 
			
		||||
    "require": {
 | 
			
		||||
        "php": "^8.1|^8.2",
 | 
			
		||||
        "php": "^8.2",
 | 
			
		||||
        "ext-dom": "*",
 | 
			
		||||
        "ext-json": "*",
 | 
			
		||||
        "ext-libxml": "*",
 | 
			
		||||
@ -56,7 +56,6 @@
 | 
			
		||||
        "hashids/hashids": "^4.0",
 | 
			
		||||
        "hedii/laravel-gelf-logger": "^8",
 | 
			
		||||
        "horstoeko/zugferd": "^1",
 | 
			
		||||
        "horstoeko/orderx": "^1",
 | 
			
		||||
        "imdhemy/laravel-purchases": "^1.7",
 | 
			
		||||
        "intervention/image": "^2.5",
 | 
			
		||||
        "invoiceninja/inspector": "^2.0",
 | 
			
		||||
@ -87,7 +86,6 @@
 | 
			
		||||
        "sentry/sentry-laravel": "^3",
 | 
			
		||||
        "setasign/fpdf": "^1.8",
 | 
			
		||||
        "setasign/fpdi": "^2.3",
 | 
			
		||||
        "shopify/shopify-api": "^4.3",
 | 
			
		||||
        "socialiteproviders/apple": "dev-master",
 | 
			
		||||
        "socialiteproviders/microsoft": "^4.1",
 | 
			
		||||
        "spatie/laravel-data": "^3.5",
 | 
			
		||||
@ -103,12 +101,13 @@
 | 
			
		||||
        "twig/twig": "^3",
 | 
			
		||||
        "twilio/sdk": "^6.40",
 | 
			
		||||
        "webpatser/laravel-countries": "dev-master#75992ad",
 | 
			
		||||
        "wepay/php-sdk": "^0.3",
 | 
			
		||||
        "wildbit/postmark-php": "^4.0",
 | 
			
		||||
        "hyvor/php-json-exporter": "^0.0.3"
 | 
			
		||||
        "hyvor/php-json-exporter": "^0.0.3",
 | 
			
		||||
        "invoiceninja/einvoice": "dev-main",
 | 
			
		||||
        "horstoeko/orderx": "dev-master"
 | 
			
		||||
    },
 | 
			
		||||
    "require-dev": {
 | 
			
		||||
        "php": "^8.1|^8.2",
 | 
			
		||||
        "php": "^8.2",
 | 
			
		||||
        "barryvdh/laravel-debugbar": "^3.6",
 | 
			
		||||
        "barryvdh/laravel-ide-helper": "^2.13",
 | 
			
		||||
        "beyondcode/laravel-query-detector": "^1.8",
 | 
			
		||||
@ -120,7 +119,7 @@
 | 
			
		||||
        "mockery/mockery": "^1.4.4",
 | 
			
		||||
        "nunomaduro/collision": "^7.0",
 | 
			
		||||
        "phpstan/phpstan": "^1.9",
 | 
			
		||||
        "phpunit/phpunit": "^10.0",
 | 
			
		||||
        "phpunit/phpunit": "^10",
 | 
			
		||||
        "spatie/laravel-ignition": "^2.0",
 | 
			
		||||
        "spaze/phpstan-stripe": "^3.0"
 | 
			
		||||
    },
 | 
			
		||||
@ -184,6 +183,10 @@
 | 
			
		||||
        {
 | 
			
		||||
            "type":"vcs",
 | 
			
		||||
            "url": "https://github.com/invoiceninja/einvoice"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "type": "vcs",
 | 
			
		||||
            "url": "https://github.com/turbo124/orderx"
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "minimum-stability": "dev",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										922
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										922
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -54,17 +54,14 @@ return [
 | 
			
		||||
        'hosted' => env('TERMS_OF_SERVICE_URL', 'https://www.invoiceninja.com/terms/'),
 | 
			
		||||
        'selfhost' => env('TERMS_OF_SERVICE_URL', 'https://www.invoiceninja.com/self-hosting-terms-service/'),
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'privacy_policy_url' => [
 | 
			
		||||
        'hosted' => env('PRIVACY_POLICY_URL', 'https://www.invoiceninja.com/privacy-policy/'),
 | 
			
		||||
        'selfhost' => env('PRIVACY_POLICY_URL', 'https://www.invoiceninja.com/self-hosting-privacy-data-control/'),
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'db' => [
 | 
			
		||||
        'multi_db_enabled' => env('MULTI_DB_ENABLED', false),
 | 
			
		||||
        'default' => env('DB_CONNECTION', 'mysql'),
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'i18n' => [
 | 
			
		||||
        'timezone_id' => env('DEFAULT_TIMEZONE', 1),
 | 
			
		||||
        'country_id' => env('DEFAULT_COUNTRY', 840), // United Stated
 | 
			
		||||
@ -79,7 +76,6 @@ return [
 | 
			
		||||
        'first_day_of_week' => env('FIRST_DATE_OF_WEEK', 0),
 | 
			
		||||
        'first_month_of_year' => env('FIRST_MONTH_OF_YEAR', '2000-01-01'),
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'testvars' => [
 | 
			
		||||
        'username' => 'user@example.com',
 | 
			
		||||
        'clientname' => 'client@example.com',
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@ namespace Database\Factories;
 | 
			
		||||
 | 
			
		||||
use App\DataMapper\ClientSettings;
 | 
			
		||||
use Illuminate\Database\Eloquent\Factories\Factory;
 | 
			
		||||
use Str;
 | 
			
		||||
 | 
			
		||||
class ClientFactory extends Factory
 | 
			
		||||
{
 | 
			
		||||
@ -49,6 +50,7 @@ class ClientFactory extends Factory
 | 
			
		||||
            'shipping_country_id' => 4,
 | 
			
		||||
            'settings' => ClientSettings::defaults(),
 | 
			
		||||
            'client_hash' => \Illuminate\Support\Str::random(40),
 | 
			
		||||
            'routing_id' => rand(100000,200000),
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,28 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use App\Models\Currency;
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
use Illuminate\Support\Facades\Schema;
 | 
			
		||||
 | 
			
		||||
return new class extends Migration
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Run the migrations.
 | 
			
		||||
     */
 | 
			
		||||
    public function up(): void
 | 
			
		||||
    {
 | 
			
		||||
        if($currency = Currency::find(89)){
 | 
			
		||||
            $currency->precision = 3;
 | 
			
		||||
            $currency->save();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reverse the migrations.
 | 
			
		||||
     */
 | 
			
		||||
    public function down(): void
 | 
			
		||||
    {
 | 
			
		||||
        //
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@ -111,7 +111,7 @@ class CurrenciesSeeder extends Seeder
 | 
			
		||||
            ['id' => 86, 'name' => 'CFP Franc', 'code' => 'XPF', 'symbol' => '', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], // precision should be zero
 | 
			
		||||
            ['id' => 87, 'name' => 'Mauritian Rupee', 'code' => 'MUR', 'symbol' => 'Rs', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
 | 
			
		||||
            ['id' => 88, 'name' => 'Cape Verdean Escudo', 'code' => 'CVE', 'symbol' => '', 'precision' => '2', 'thousand_separator' => '.', 'decimal_separator' => '$'],
 | 
			
		||||
            ['id' => 89, 'name' => 'Kuwaiti Dinar', 'code' => 'KWD', 'symbol' => 'KD', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
 | 
			
		||||
            ['id' => 89, 'name' => 'Kuwaiti Dinar', 'code' => 'KWD', 'symbol' => 'KD', 'precision' => '3', 'thousand_separator' => ',', 'decimal_separator' => '.'],
 | 
			
		||||
            ['id' => 90, 'name' => 'Algerian Dinar', 'code' => 'DZD', 'symbol' => 'DA', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
 | 
			
		||||
            ['id' => 91, 'name' => 'Macedonian Denar', 'code' => 'MKD', 'symbol' => 'ден', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
 | 
			
		||||
            ['id' => 92, 'name' => 'Fijian Dollar', 'code' => 'FJD', 'symbol' => 'FJ$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
 | 
			
		||||
 | 
			
		||||
@ -5328,6 +5328,9 @@ $lang = array(
 | 
			
		||||
    'disconnected' => 'Disconnected',
 | 
			
		||||
    'reconnect' => 'Reconnect',
 | 
			
		||||
    'e_invoice_settings' => 'E-Invoice Settings',
 | 
			
		||||
    'currency_mauritanian_ouguiya' => 'Mauritanian Ouguiya',
 | 
			
		||||
    'currency_bhutan_ngultrum' => 'Bhutan Ngultrum',
 | 
			
		||||
    
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
return $lang;
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@
 | 
			
		||||
    <env name="MAIL_MAILER" value="array"/>
 | 
			
		||||
    <env name="DB_CONNECTION" value="sqlite"/>
 | 
			
		||||
    <env name="DB_DATABASE" value=":memory:"/>
 | 
			
		||||
    <env name="DB_STRICT" value="FALSE"/>
 | 
			
		||||
    <env name="DB_STRICT" value="false"/>
 | 
			
		||||
  </php>
 | 
			
		||||
  <logging/>
 | 
			
		||||
</phpunit>
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,6 @@ use App\Http\Controllers\Gateways\Mollie3dsController;
 | 
			
		||||
use App\Http\Controllers\SetupController;
 | 
			
		||||
use App\Http\Controllers\StripeConnectController;
 | 
			
		||||
use App\Http\Controllers\UserController;
 | 
			
		||||
use App\Http\Controllers\WePayController;
 | 
			
		||||
use Illuminate\Support\Facades\Route;
 | 
			
		||||
 | 
			
		||||
Route::get('/', [BaseController::class, 'flutterRoute'])->middleware('guest');
 | 
			
		||||
@ -31,9 +30,6 @@ Route::post('password/email', [ForgotPasswordController::class, 'sendResetLinkEm
 | 
			
		||||
Route::get('password/reset/{token}', [ResetPasswordController::class, 'showResetForm'])->middleware(['domain_db', 'email_db'])->name('password.reset');
 | 
			
		||||
Route::post('password/reset', [ResetPasswordController::class, 'reset'])->middleware('email_db')->name('password.update');
 | 
			
		||||
 | 
			
		||||
Route::get('wepay/signup/{token}', [WePayController::class, 'signup'])->name('wepay.signup');
 | 
			
		||||
Route::get('wepay/finished', [WePayController::class, 'finished'])->name('wepay.finished');
 | 
			
		||||
 | 
			
		||||
Route::get('auth/{provider}', [LoginController::class, 'redirectToProvider']);
 | 
			
		||||
 | 
			
		||||
Route::middleware('url_db')->group(function () {
 | 
			
		||||
 | 
			
		||||
@ -11,13 +11,14 @@
 | 
			
		||||
 | 
			
		||||
namespace Tests\Feature;
 | 
			
		||||
 | 
			
		||||
use App\Models\CompanyGateway;
 | 
			
		||||
use Tests\TestCase;
 | 
			
		||||
use Tests\MockAccountData;
 | 
			
		||||
use App\Models\GatewayType;
 | 
			
		||||
use App\Models\CompanyGateway;
 | 
			
		||||
use App\Utils\Traits\MakesHash;
 | 
			
		||||
use Illuminate\Database\Eloquent\Model;
 | 
			
		||||
use Illuminate\Routing\Middleware\ThrottleRequests;
 | 
			
		||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
 | 
			
		||||
use Tests\MockAccountData;
 | 
			
		||||
use Tests\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @test
 | 
			
		||||
@ -29,6 +30,9 @@ class ClientGatewayTokenApiTest extends TestCase
 | 
			
		||||
    use DatabaseTransactions;
 | 
			
		||||
    use MockAccountData;
 | 
			
		||||
 | 
			
		||||
    protected $faker;
 | 
			
		||||
    protected CompanyGateway $cg;
 | 
			
		||||
    
 | 
			
		||||
    protected function setUp() :void
 | 
			
		||||
    {
 | 
			
		||||
        parent::setUp();
 | 
			
		||||
 | 
			
		||||
@ -11,11 +11,21 @@
 | 
			
		||||
 | 
			
		||||
namespace Tests\Feature\EInvoice;
 | 
			
		||||
 | 
			
		||||
use App\Services\EDocument\Standards\FatturaPA;
 | 
			
		||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
 | 
			
		||||
use Illuminate\Routing\Middleware\ThrottleRequests;
 | 
			
		||||
use Tests\MockAccountData;
 | 
			
		||||
use Tests\TestCase;
 | 
			
		||||
use App\Models\Client;
 | 
			
		||||
use App\Models\Company;
 | 
			
		||||
use Tests\MockAccountData;
 | 
			
		||||
use App\DataMapper\ClientSettings;
 | 
			
		||||
use App\DataMapper\CompanySettings;
 | 
			
		||||
use App\DataMapper\InvoiceItem;
 | 
			
		||||
use App\Models\Invoice;
 | 
			
		||||
use Invoiceninja\Einvoice\Symfony\Encode;
 | 
			
		||||
use App\Services\EDocument\Standards\FatturaPANew;
 | 
			
		||||
use Illuminate\Routing\Middleware\ThrottleRequests;
 | 
			
		||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\FatturaElettronica;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\FatturaElettronicaBodyType\FatturaElettronicaBody;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FatturaPA\FatturaElettronicaHeaderType\FatturaElettronicaHeader;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @test
 | 
			
		||||
@ -31,6 +41,9 @@ class FatturaPATest extends TestCase
 | 
			
		||||
 | 
			
		||||
        $this->makeTestData();
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        $this->markTestSkipped('prevent running in CI');
 | 
			
		||||
 | 
			
		||||
        $this->withoutMiddleware(
 | 
			
		||||
            ThrottleRequests::class
 | 
			
		||||
        );
 | 
			
		||||
@ -38,9 +51,86 @@ class FatturaPATest extends TestCase
 | 
			
		||||
 | 
			
		||||
    public function testInvoiceBoot()
 | 
			
		||||
    {
 | 
			
		||||
        $fat = new FatturaPA($this->invoice);
 | 
			
		||||
        $xml = $fat->run();
 | 
			
		||||
 | 
			
		||||
        $this->assertnotNull($xml);
 | 
			
		||||
        $settings = CompanySettings::defaults();
 | 
			
		||||
        $settings->address1 = 'Via Silvio Spaventa 108';
 | 
			
		||||
        $settings->city = 'Calcinelli';
 | 
			
		||||
 | 
			
		||||
$settings->state = 'PA';
 | 
			
		||||
 | 
			
		||||
// $settings->state = 'Perugia';
 | 
			
		||||
        $settings->postal_code = '61030'; 
 | 
			
		||||
        $settings->country_id = '380';
 | 
			
		||||
        $settings->currency_id = '3';
 | 
			
		||||
        $settings->vat_number = '01234567890';
 | 
			
		||||
        $settings->id_number = '';
 | 
			
		||||
 | 
			
		||||
        $company = Company::factory()->create([   
 | 
			
		||||
            'account_id' => $this->account->id,
 | 
			
		||||
            'settings' => $settings,
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $client_settings = ClientSettings::defaults();
 | 
			
		||||
        $client_settings->currency_id = '3';
 | 
			
		||||
        
 | 
			
		||||
        $client = Client::factory()->create([
 | 
			
		||||
            'company_id' => $company->id,
 | 
			
		||||
            'user_id' => $this->user->id,
 | 
			
		||||
            'name' => 'Italian Client Name',
 | 
			
		||||
            'address1' => 'Via Antonio da Legnago 68',
 | 
			
		||||
            'city' => 'Monasterace',
 | 
			
		||||
            'state' => 'CR',
 | 
			
		||||
            // 'state' => 'Reggio Calabria',
 | 
			
		||||
            'postal_code' => '89040',
 | 
			
		||||
            'country_id' => 380,
 | 
			
		||||
            'routing_id' => 'ABC1234',
 | 
			
		||||
            'settings' => $client_settings,
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $item = new InvoiceItem;
 | 
			
		||||
        $item->product_key = "Product Key";
 | 
			
		||||
        $item->notes = "Product Description";
 | 
			
		||||
        $item->cost = 10;
 | 
			
		||||
        $item->quantity = 10;
 | 
			
		||||
        $item->tax_rate1 = 22;
 | 
			
		||||
        $item->tax_name1 = 'IVA';
 | 
			
		||||
        
 | 
			
		||||
        $invoice = Invoice::factory()->create([
 | 
			
		||||
            'company_id' => $company->id,
 | 
			
		||||
            'user_id' => $this->user->id,
 | 
			
		||||
            'client_id' => $client->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' => 'ITA-'.rand(1000,100000)
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $invoice->service()->markSent()->save();
 | 
			
		||||
 | 
			
		||||
        $fat = new FatturaPANew($invoice);
 | 
			
		||||
        $fat->run();
 | 
			
		||||
 | 
			
		||||
        $fe = $fat->getFatturaElettronica();
 | 
			
		||||
 | 
			
		||||
        $this->assertNotNull($fe);
 | 
			
		||||
 | 
			
		||||
        $this->assertInstanceOf(FatturaElettronica::class, $fe);
 | 
			
		||||
        $this->assertInstanceOf(FatturaElettronicaBody::class, $fe->FatturaElettronicaBody[0]);
 | 
			
		||||
        $this->assertInstanceOf(FatturaElettronicaHeader::class, $fe->FatturaElettronicaHeader);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $encoder = new Encode($fe);
 | 
			
		||||
        $xml = $encoder->toXml();
 | 
			
		||||
 | 
			
		||||
        $this->assertNotNull($xml);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										452
									
								
								tests/Integration/Einvoice/Fact1Test.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										452
									
								
								tests/Integration/Einvoice/Fact1Test.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,452 @@
 | 
			
		||||
<?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\Integration\Einvoice;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use Tests\TestCase;
 | 
			
		||||
use App\Models\Client;
 | 
			
		||||
use App\Models\Invoice;
 | 
			
		||||
use Tests\MockAccountData;
 | 
			
		||||
use App\Models\ClientContact;
 | 
			
		||||
use App\DataMapper\InvoiceItem;
 | 
			
		||||
use App\DataMapper\ClientSettings;
 | 
			
		||||
use Illuminate\Support\Facades\Cache;
 | 
			
		||||
use Symfony\Component\Validator\Validation;
 | 
			
		||||
use Symfony\Component\Serializer\Serializer;
 | 
			
		||||
use Symfony\Component\Serializer\Encoder\XmlEncoder;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\ItemType\Item;
 | 
			
		||||
use Symfony\Component\Serializer\Encoder\JsonEncoder;
 | 
			
		||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\PartyType\Party;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\PriceType\Price;
 | 
			
		||||
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\ContactType\Contact;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\CountryType\Country;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\AmountType\TaxAmount;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\TaxTotalType\TaxTotal;
 | 
			
		||||
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
 | 
			
		||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\AmountType\PriceAmount;
 | 
			
		||||
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\TaxSchemeType\TaxScheme;
 | 
			
		||||
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
 | 
			
		||||
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\AddressType\PostalAddress;
 | 
			
		||||
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\InvoiceLineType\InvoiceLine;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\TaxScheme as FACT1TaxScheme;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\TaxSubtotalType\TaxSubtotal;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\QuantityType\InvoicedQuantity;
 | 
			
		||||
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\AmountType\LineExtensionAmount;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\AmountType\PayableAmount;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\AmountType\TaxableAmount;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\AmountType\TaxExclusiveAmount;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\AmountType\TaxInclusiveAmount;
 | 
			
		||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\PartyTaxSchemeType\PartyTaxScheme;
 | 
			
		||||
use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\MonetaryTotalType\LegalMonetaryTotal;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\PartyLegalEntityType\PartyLegalEntity;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\TaxCategoryType\ClassifiedTaxCategory;
 | 
			
		||||
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\CustomerPartyType\AccountingCustomerParty;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\SupplierPartyType\AccountingSupplierParty;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\PartyIdentificationType\PartyIdentification;
 | 
			
		||||
use Invoiceninja\Einvoice\Models\FACT1\TaxCategoryType\TaxCategory;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @test
 | 
			
		||||
 */
 | 
			
		||||
class Fact1Test extends TestCase
 | 
			
		||||
{
 | 
			
		||||
    use MockAccountData;
 | 
			
		||||
    use DatabaseTransactions;
 | 
			
		||||
 | 
			
		||||
    protected function setUp(): void
 | 
			
		||||
    {
 | 
			
		||||
        parent::setUp();
 | 
			
		||||
 | 
			
		||||
        $this->markTestSkipped('prevent running in CI');
 | 
			
		||||
 | 
			
		||||
        $this->makeTestData();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testRoBuild()
 | 
			
		||||
    {
 | 
			
		||||
        $settings = $this->company->settings;
 | 
			
		||||
        $settings->currency_id = '42';
 | 
			
		||||
        $this->company->saveSettings($settings, $this->company);
 | 
			
		||||
        $this->company->save();
 | 
			
		||||
 | 
			
		||||
        $settings = ClientSettings::defaults();
 | 
			
		||||
        $settings->currency_id = '42';
 | 
			
		||||
 | 
			
		||||
//VAT
 | 
			
		||||
//19%
 | 
			
		||||
        $client = Client::factory()
 | 
			
		||||
        ->create([
 | 
			
		||||
            'user_id' => $this->user->id,
 | 
			
		||||
            'company_id' => $this->company->id,
 | 
			
		||||
            'id_number' => '646546549',
 | 
			
		||||
            'address1' => '40D, Șoseaua București-Ploiești',
 | 
			
		||||
            'city' => 'SECTOR3',
 | 
			
		||||
            'state' => 'RO-B',
 | 
			
		||||
            'country_id' => 642,
 | 
			
		||||
            'vat_number' => 646546549,
 | 
			
		||||
            'name' => 'Client Company Name',
 | 
			
		||||
            'settings' => $settings,
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        ClientContact::factory()->create([
 | 
			
		||||
            'user_id' => $this->user->id,
 | 
			
		||||
            'company_id' => $this->company->id,
 | 
			
		||||
            'client_id' => $client->id,
 | 
			
		||||
            'first_name' => 'Bob',
 | 
			
		||||
            'last_name' => 'Jane',
 | 
			
		||||
            'email' => 'bob@gmail.com',
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        $items = [];
 | 
			
		||||
 | 
			
		||||
        $item = new InvoiceItem;
 | 
			
		||||
        $item->cost = 10;
 | 
			
		||||
        $item->quantity = 10;
 | 
			
		||||
        $item->tax_name1 = 'VAT';
 | 
			
		||||
        $item->tax_rate1 = '19';
 | 
			
		||||
        $item->product_key = "Product Name";
 | 
			
		||||
        $item->notes = "A great product description";
 | 
			
		||||
 | 
			
		||||
        $_invoice = Invoice::factory()->create([
 | 
			
		||||
            'user_id' => $this->user->id,
 | 
			
		||||
            'company_id' => $this->company->id,
 | 
			
		||||
            'client_id' => $client->id,
 | 
			
		||||
            'number' => 'INV-'.rand(1000,1000000),
 | 
			
		||||
            'line_items' => [$item],
 | 
			
		||||
            'due_date' => now()->addDays(20)->format('Y-m-d'),
 | 
			
		||||
            'status_id' => 1,
 | 
			
		||||
            'discount' => 0,
 | 
			
		||||
        ]);
 | 
			
		||||
        
 | 
			
		||||
        $_invoice->service()->markSent()->save();
 | 
			
		||||
        $calc = $_invoice->calc();
 | 
			
		||||
        
 | 
			
		||||
        $invoice = new \Invoiceninja\Einvoice\Models\FACT1\Invoice();
 | 
			
		||||
        $invoice->UBLVersionID = '2.1';
 | 
			
		||||
        $invoice->CustomizationID = 'urn:cen.eu:en16931:2017#compliant#urn:efactura.mfinante.ro:CIUS-RO:1.0.1';
 | 
			
		||||
        $invoice->ID = $_invoice->number;
 | 
			
		||||
        $invoice->InvoiceTypeCode = 380;
 | 
			
		||||
        $invoice->IssueDate = new DateTime($_invoice->date);
 | 
			
		||||
        $invoice->DueDate = new DateTime($_invoice->due_date);
 | 
			
		||||
        $invoice->DocumentCurrencyCode = 'RON';
 | 
			
		||||
        $invoice->TaxCurrencyCode = 'RON';
 | 
			
		||||
 | 
			
		||||
        $asp = new AccountingSupplierParty();
 | 
			
		||||
        $party = new Party();
 | 
			
		||||
        
 | 
			
		||||
        $party_identification = new PartyIdentification();
 | 
			
		||||
        $party_identification->ID = 'company_id_number';
 | 
			
		||||
        $party->PartyIdentification[] = $party_identification;
 | 
			
		||||
        
 | 
			
		||||
        $sp_address = new PostalAddress();
 | 
			
		||||
        $sp_address->StreetName = $this->company->settings->address1;
 | 
			
		||||
        $sp_address->CityName = 'SECTOR2';
 | 
			
		||||
        $sp_address->CountrySubentity = 'RO-B';
 | 
			
		||||
 | 
			
		||||
        $country = new Country();
 | 
			
		||||
        $country->IdentificationCode='RO';
 | 
			
		||||
        $sp_address->Country = $country;
 | 
			
		||||
 | 
			
		||||
        $party->PostalAddress = $sp_address;
 | 
			
		||||
 | 
			
		||||
        $pts = new PartyTaxScheme();
 | 
			
		||||
        $tax_scheme = new TaxScheme();
 | 
			
		||||
        $tax_scheme->ID = 'VAT';
 | 
			
		||||
 | 
			
		||||
        $pts->CompanyID = 'RO234234234';
 | 
			
		||||
        $pts->TaxScheme = $tax_scheme;
 | 
			
		||||
 | 
			
		||||
        $party->PartyTaxScheme[] = $pts;
 | 
			
		||||
 | 
			
		||||
        $ple = new PartyLegalEntity();
 | 
			
		||||
        $ple->RegistrationName = $this->company->settings->name;
 | 
			
		||||
        $ple->CompanyID = 'J40/2222/2009';
 | 
			
		||||
 | 
			
		||||
        $party->PartyLegalEntity[] = $ple;
 | 
			
		||||
 | 
			
		||||
        $p_contact = new Contact();
 | 
			
		||||
        $p_contact->Name = $this->company->owner()->present()->name();
 | 
			
		||||
        $p_contact->Telephone = $this->company->settings->phone;
 | 
			
		||||
        $p_contact->ElectronicMail = $this->company->owner()->email;
 | 
			
		||||
        $party->Contact = $p_contact;
 | 
			
		||||
        $asp->Party = $party;
 | 
			
		||||
 | 
			
		||||
        $invoice->AccountingSupplierParty = $asp;
 | 
			
		||||
 | 
			
		||||
        $acp = new AccountingCustomerParty();
 | 
			
		||||
 | 
			
		||||
        $party = new Party();
 | 
			
		||||
 | 
			
		||||
        $party_identification = new PartyIdentification();
 | 
			
		||||
        $party_identification->ID = 'client_id_number';
 | 
			
		||||
        $party->PartyIdentification[] = $party_identification;
 | 
			
		||||
 | 
			
		||||
        $sp_address = new PostalAddress();
 | 
			
		||||
        $sp_address->StreetName = $client->address1;
 | 
			
		||||
        $sp_address->CityName = 'SECTOR2';
 | 
			
		||||
        $sp_address->CountrySubentity = 'RO-B';
 | 
			
		||||
 | 
			
		||||
        $country = new Country();
 | 
			
		||||
        $country->IdentificationCode = 'RO';
 | 
			
		||||
        $sp_address->Country = $country;
 | 
			
		||||
 | 
			
		||||
        $party->PostalAddress = $sp_address;
 | 
			
		||||
 | 
			
		||||
        $ple = new PartyLegalEntity();
 | 
			
		||||
        $ple->RegistrationName = $client->name;
 | 
			
		||||
        $ple->CompanyID = '646546549';
 | 
			
		||||
 | 
			
		||||
        $party->PartyLegalEntity[] = $ple;
 | 
			
		||||
 | 
			
		||||
        $p_contact = new Contact();
 | 
			
		||||
        $p_contact->Name = $client->contacts->first()->present()->name();
 | 
			
		||||
        $p_contact->Telephone = $client->contacts->first()->present()->phone();
 | 
			
		||||
        $p_contact->ElectronicMail = $client->contacts->first()->present()->email();
 | 
			
		||||
 | 
			
		||||
        $party->Contact = $p_contact;
 | 
			
		||||
 | 
			
		||||
        $acp->Party = $party;
 | 
			
		||||
        $invoice->AccountingCustomerParty = $acp;
 | 
			
		||||
 | 
			
		||||
        $taxtotal = new TaxTotal();
 | 
			
		||||
        $tax_amount = new TaxAmount();
 | 
			
		||||
 | 
			
		||||
        $tax_amount->amount = $calc->getItemTotalTaxes();
 | 
			
		||||
        $tax_amount->currencyID = $_invoice->client->currency()->code;
 | 
			
		||||
 | 
			
		||||
        $tc = new TaxCategory();
 | 
			
		||||
        $tc->ID = "S";
 | 
			
		||||
 | 
			
		||||
        $taxable = $this->getTaxable($_invoice);
 | 
			
		||||
        
 | 
			
		||||
        $taxable_amount = new TaxableAmount();
 | 
			
		||||
        $taxable_amount->amount = $taxable;
 | 
			
		||||
        $taxable_amount->currencyID = $_invoice->client->currency()->code;
 | 
			
		||||
 | 
			
		||||
        $tax_sub_total = new TaxSubtotal();
 | 
			
		||||
        $tax_sub_total->TaxAmount = $tax_amount;
 | 
			
		||||
        $tax_sub_total->TaxCategory = $tc;
 | 
			
		||||
        $tax_sub_total->TaxableAmount = $taxable_amount;
 | 
			
		||||
        $taxtotal->TaxSubtotal[] = $tax_sub_total;
 | 
			
		||||
        
 | 
			
		||||
        $invoice->TaxTotal[] = $taxtotal;
 | 
			
		||||
        
 | 
			
		||||
        $lmt = new LegalMonetaryTotal();
 | 
			
		||||
 | 
			
		||||
        $lea = new LineExtensionAmount();
 | 
			
		||||
        $lea->amount = $taxable;
 | 
			
		||||
        $lea->currencyID = $_invoice->client->currency()->code;
 | 
			
		||||
 | 
			
		||||
        $lmt->LineExtensionAmount = $lea;
 | 
			
		||||
 | 
			
		||||
        $tea = new TaxExclusiveAmount;
 | 
			
		||||
        $tea->amount = $taxable;
 | 
			
		||||
        $tea->currencyID = $_invoice->client->currency()->code;
 | 
			
		||||
 | 
			
		||||
        $lmt->TaxExclusiveAmount = $tea;
 | 
			
		||||
 | 
			
		||||
        $tia = new TaxInclusiveAmount;
 | 
			
		||||
        $tia->amount = $_invoice->amount;
 | 
			
		||||
        $tia->currencyID = $_invoice->client->currency()->code;
 | 
			
		||||
 | 
			
		||||
        $lmt->TaxInclusiveAmount = $tia;
 | 
			
		||||
 | 
			
		||||
        $pa = new PayableAmount;
 | 
			
		||||
        $pa->amount = $_invoice->amount;
 | 
			
		||||
        $pa->currencyID = $_invoice->client->currency()->code;
 | 
			
		||||
 | 
			
		||||
        $lmt->PayableAmount = $pa;
 | 
			
		||||
        $invoice->LegalMonetaryTotal = $lmt;
 | 
			
		||||
 | 
			
		||||
        foreach($_invoice->line_items as $key => $item)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            $invoice_line = new InvoiceLine;
 | 
			
		||||
            $invoice_line->ID = $key++;
 | 
			
		||||
 | 
			
		||||
            $iq = new InvoicedQuantity();
 | 
			
		||||
            $iq->amount = $item->cost;
 | 
			
		||||
            $iq->unitCode = 'H87';
 | 
			
		||||
            
 | 
			
		||||
            $invoice_line->InvoicedQuantity = $iq;
 | 
			
		||||
 | 
			
		||||
            $invoice_line->Note = substr($item->notes, 0, 200);
 | 
			
		||||
 | 
			
		||||
            $ctc = new ClassifiedTaxCategory();
 | 
			
		||||
            $ctc->ID = 'S';
 | 
			
		||||
 | 
			
		||||
            $i = new Item;
 | 
			
		||||
            $i->Description = $item->notes;
 | 
			
		||||
            $i->Name = $item->product_key;
 | 
			
		||||
 | 
			
		||||
            $tax_scheme = new FACT1TaxScheme();
 | 
			
		||||
            $tax_scheme->ID = $item->tax_name1;
 | 
			
		||||
            $tax_scheme->Name = $item->tax_rate1;
 | 
			
		||||
 | 
			
		||||
            $ctc = new ClassifiedTaxCategory();
 | 
			
		||||
            $ctc->TaxScheme = $tax_scheme;
 | 
			
		||||
            $ctc->ID = 'S';
 | 
			
		||||
 | 
			
		||||
            $i->ClassifiedTaxCategory[] = $ctc;
 | 
			
		||||
 | 
			
		||||
            $invoice_line->Item = $i;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            $lea = new LineExtensionAmount;
 | 
			
		||||
            $lea->amount = $item->line_total;
 | 
			
		||||
            $lea->currencyID = $_invoice->client->currency()->code;
 | 
			
		||||
 | 
			
		||||
            $invoice_line->LineExtensionAmount = $lea;
 | 
			
		||||
 | 
			
		||||
            $price = new Price();
 | 
			
		||||
            $pa = new PriceAmount();
 | 
			
		||||
            $pa->amount = $item->line_total;
 | 
			
		||||
            $pa->currencyID = $_invoice->client->currency()->code;
 | 
			
		||||
 | 
			
		||||
            $price->PriceAmount = $pa;
 | 
			
		||||
 | 
			
		||||
            $lea = new LineExtensionAmount();
 | 
			
		||||
            $lea->amount = $item->line_total;
 | 
			
		||||
            $lea->currencyID = $_invoice->client->currency()->code;
 | 
			
		||||
            
 | 
			
		||||
            $invoice_line->LineExtensionAmount = $lea;
 | 
			
		||||
 | 
			
		||||
            $invoice->InvoiceLine[] = $invoice_line;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $validator = Validation::createValidatorBuilder()
 | 
			
		||||
            ->enableAttributeMapping()
 | 
			
		||||
            ->getValidator();
 | 
			
		||||
 | 
			
		||||
        $errors = $validator->validate($invoice);
 | 
			
		||||
 | 
			
		||||
        foreach($errors as $error) {
 | 
			
		||||
            // echo $error->getPropertyPath() . ': ' . $error->getMessage() . "\n";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->assertCount(0, $errors);
 | 
			
		||||
 | 
			
		||||
        $phpDocExtractor = new PhpDocExtractor();
 | 
			
		||||
        $reflectionExtractor = new ReflectionExtractor();
 | 
			
		||||
        // list of PropertyListExtractorInterface (any iterable)
 | 
			
		||||
        $listExtractors = [$reflectionExtractor];
 | 
			
		||||
        // list of PropertyTypeExtractorInterface (any iterable)
 | 
			
		||||
        $typeExtractors = [$reflectionExtractor,$phpDocExtractor];
 | 
			
		||||
        // list of PropertyDescriptionExtractorInterface (any iterable)
 | 
			
		||||
        $descriptionExtractors = [$phpDocExtractor];
 | 
			
		||||
        // list of PropertyAccessExtractorInterface (any iterable)
 | 
			
		||||
        $accessExtractors = [$reflectionExtractor];
 | 
			
		||||
        // list of PropertyInitializableExtractorInterface (any iterable)
 | 
			
		||||
        $propertyInitializableExtractors = [$reflectionExtractor];
 | 
			
		||||
        $propertyInfo = new PropertyInfoExtractor(
 | 
			
		||||
            // $listExtractors,
 | 
			
		||||
            $propertyInitializableExtractors,
 | 
			
		||||
            $descriptionExtractors,
 | 
			
		||||
            $typeExtractors,
 | 
			
		||||
            // $accessExtractors,
 | 
			
		||||
        );
 | 
			
		||||
        $context = [
 | 
			
		||||
            'xml_format_output' => true,
 | 
			
		||||
            'remove_empty_tags' => true,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $encoder = new XmlEncoder($context);
 | 
			
		||||
        $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
 | 
			
		||||
        $metadataAwareNameConverter = new MetadataAwareNameConverter($classMetadataFactory);
 | 
			
		||||
        $discriminator = new ClassDiscriminatorFromClassMetadata($classMetadataFactory);
 | 
			
		||||
 | 
			
		||||
        $normalizer = new ObjectNormalizer($classMetadataFactory, $metadataAwareNameConverter, null, $propertyInfo);
 | 
			
		||||
        
 | 
			
		||||
        $normalizers = [  new DateTimeNormalizer(), $normalizer,  new ArrayDenormalizer() , ];
 | 
			
		||||
        $encoders = [$encoder, new JsonEncoder()];
 | 
			
		||||
        $serializer = new Serializer($normalizers, $encoders);
 | 
			
		||||
 | 
			
		||||
        $n_context = [
 | 
			
		||||
            AbstractObjectNormalizer::SKIP_NULL_VALUES => true,
 | 
			
		||||
            // AbstractObjectNormalizer::SKIP_UNINITIALIZED_VALUES => true,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        // $invoice = $normalizer->normalize($invoice, 'json', $n_context);
 | 
			
		||||
        // echo print_r($invoice);
 | 
			
		||||
        // $invoice = $serializer->serialize($invoice, 'xml', $n_context);
 | 
			
		||||
        $dataxml = $serializer->encode($invoice, 'xml', $context);
 | 
			
		||||
 | 
			
		||||
        // echo $dataxml;
 | 
			
		||||
 | 
			
		||||
        //set default standard props
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @return float|int|mixed
 | 
			
		||||
     */
 | 
			
		||||
    private function getTaxable(Invoice $invoice): float
 | 
			
		||||
    {
 | 
			
		||||
        $total = 0;
 | 
			
		||||
 | 
			
		||||
        foreach ($invoice->line_items as $item) {
 | 
			
		||||
            $line_total = $item->quantity * $item->cost;
 | 
			
		||||
 | 
			
		||||
            if ($item->discount != 0) {
 | 
			
		||||
                if ($invoice->is_amount_discount) {
 | 
			
		||||
                    $line_total -= $item->discount;
 | 
			
		||||
                } else {
 | 
			
		||||
                    $line_total -= $line_total * $item->discount / 100;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $total += $line_total;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($invoice->discount > 0) {
 | 
			
		||||
            if ($invoice->is_amount_discount) {
 | 
			
		||||
                $total -= $invoice->discount;
 | 
			
		||||
            } else {
 | 
			
		||||
                $total *= (100 - $invoice->discount) / 100;
 | 
			
		||||
                $total = round($total, 2);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($invoice->custom_surcharge1 && $invoice->custom_surcharge_tax1) {
 | 
			
		||||
            $total += $invoice->custom_surcharge1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($invoice->custom_surcharge2 && $invoice->custom_surcharge_tax2) {
 | 
			
		||||
            $total += $invoice->custom_surcharge2;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($invoice->custom_surcharge3 && $invoice->custom_surcharge_tax3) {
 | 
			
		||||
            $total += $invoice->custom_surcharge3;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($invoice->custom_surcharge4 && $invoice->custom_surcharge_tax4) {
 | 
			
		||||
            $total += $invoice->custom_surcharge4;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $total;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user