mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-04 08:37:32 -05:00 
			
		
		
		
	
						commit
						e18665d1e8
					
				@ -1 +1 @@
 | 
			
		||||
5.8.48
 | 
			
		||||
5.8.49
 | 
			
		||||
@ -13,11 +13,25 @@ namespace App\DataMapper\EDoc;
 | 
			
		||||
 | 
			
		||||
use Spatie\LaravelData\Data;
 | 
			
		||||
 | 
			
		||||
use Spatie\LaravelData\Optional;
 | 
			
		||||
use App\DataMapper\EDoc\FatturaPA\DatiContratto;
 | 
			
		||||
use App\DataMapper\EDoc\FatturaPA\DatiRicezione;
 | 
			
		||||
use App\DataMapper\EDoc\FatturaPA\DatiOrdineAcquisto;
 | 
			
		||||
use App\DataMapper\EDoc\FatturaPA\DatiAnagraficiVettore;
 | 
			
		||||
 | 
			
		||||
class FatturaPA extends Data
 | 
			
		||||
{
 | 
			
		||||
    public string $RegimeFiscale = 'RF01';
 | 
			
		||||
    public string $TipoDocumento = 'TD01';
 | 
			
		||||
    public string $ModalitaPagamento = 'MP01';
 | 
			
		||||
    public string $CondizioniPagamento = 'TP02';
 | 
			
		||||
    public DatiRicezione|Optional $DatiRicezione;
 | 
			
		||||
    public DatiContratto|Optional $DatiContratto;
 | 
			
		||||
    public DatiOrdineAcquisto|Optional $DatiOrdineAcquisto;
 | 
			
		||||
    public DatiAnagraficiVettore|Optional $DatiAnagraficiVettore;
 | 
			
		||||
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        public string $RegimeFiscale = 'RF01',
 | 
			
		||||
        public string $TipoDocumento = 'TD01',
 | 
			
		||||
        public string $ModalitaPagamento = 'MP01',
 | 
			
		||||
        public string $CondizioniPagamento = 'TP02',
 | 
			
		||||
    ) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										28
									
								
								app/DataMapper/EDoc/FatturaPA/DatiAnagraficiVettore.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/DataMapper/EDoc/FatturaPA/DatiAnagraficiVettore.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
<?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\DataMapper\EDoc\FatturaPA;
 | 
			
		||||
 | 
			
		||||
use Spatie\LaravelData\Data;
 | 
			
		||||
use Spatie\LaravelData\Attributes\WithTransformer;
 | 
			
		||||
use Spatie\LaravelData\Transformers\DateTimeInterfaceTransformer;
 | 
			
		||||
 | 
			
		||||
class DatiAnagraficiVettore extends Data
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        public string $IdFiscaleIVA = '',
 | 
			
		||||
        public string $CodiceFiscale = '',
 | 
			
		||||
        public string $Anagrafica = '',
 | 
			
		||||
        #[WithTransformer(DateTimeInterfaceTransformer::class, format: 'Y-m-d\TH:i:s.uP')]
 | 
			
		||||
        public \DateTime $DataOraConsegna = new \DateTime(),
 | 
			
		||||
    ){}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								app/DataMapper/EDoc/FatturaPA/DatiContratto.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/DataMapper/EDoc/FatturaPA/DatiContratto.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
<?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\DataMapper\EDoc\FatturaPA;
 | 
			
		||||
 | 
			
		||||
use Spatie\LaravelData\Data;
 | 
			
		||||
 | 
			
		||||
class DatiContratto extends Data
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        public string $RiferimentoNumeroLinea = '',
 | 
			
		||||
        public string $IdDocumento = '',
 | 
			
		||||
        public string $Data = '',
 | 
			
		||||
        public string $NumItem = '',
 | 
			
		||||
        public string $CodiceCommessaConvenzione = '',
 | 
			
		||||
        public string $CodiceCUP = '',
 | 
			
		||||
        public string $CodiceCIG = '',
 | 
			
		||||
    ) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								app/DataMapper/EDoc/FatturaPA/DatiOrdineAcquisto.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/DataMapper/EDoc/FatturaPA/DatiOrdineAcquisto.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
<?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\DataMapper\EDoc\FatturaPA;
 | 
			
		||||
 | 
			
		||||
use Spatie\LaravelData\Data;
 | 
			
		||||
 | 
			
		||||
class DatiOrdineAcquisto extends Data
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        public string $RiferimentoNumeroLinea = '',
 | 
			
		||||
        public string $IdDocumento = '',
 | 
			
		||||
        public string $Data = '',
 | 
			
		||||
        public string $NumItem = '',
 | 
			
		||||
        public string $CodiceCommessaConvenzione = '',
 | 
			
		||||
        public string $CodiceCUP = '',
 | 
			
		||||
        public string $CodiceCIG = '',
 | 
			
		||||
    ) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								app/DataMapper/EDoc/FatturaPA/DatiRicezione.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/DataMapper/EDoc/FatturaPA/DatiRicezione.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
<?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\DataMapper\EDoc\FatturaPA;
 | 
			
		||||
 | 
			
		||||
use Spatie\LaravelData\Data;
 | 
			
		||||
 | 
			
		||||
class DatiRicezione extends Data
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        public string $RiferimentoNumeroLinea = '',
 | 
			
		||||
        public string $IdDocumento = '',
 | 
			
		||||
        public string $Data = '',
 | 
			
		||||
        public string $NumItem = '',
 | 
			
		||||
        public string $CodiceCommessaConvenzione = '',
 | 
			
		||||
        public string $CodiceCUP = '',
 | 
			
		||||
        public string $CodiceCIG = '',
 | 
			
		||||
    ) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -27,47 +27,3 @@ class EDocSettings extends Data
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DatiAnagraficiVettore extends Data{
 | 
			
		||||
 | 
			
		||||
    public string $IdFiscaleIVA = '';
 | 
			
		||||
    public string $CodiceFiscale = '';
 | 
			
		||||
    public string $Anagrafica = '';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class DatiTrasporto extends Data{
 | 
			
		||||
    public string $DataOraConsegna = ''; //datetime in this format 2017-01-10T16:46:12.000+02:00
 | 
			
		||||
    //public DatiAnagraficiVettore
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class DatiOrdineAcquisto extends Data{
 | 
			
		||||
    public string $RiferimentoNumeroLinea = '';
 | 
			
		||||
    public string $IdDocumento = '';
 | 
			
		||||
    public string $Data = '';
 | 
			
		||||
    public string $NumItem = '';
 | 
			
		||||
    public string $CodiceCommessaConvenzione = '';
 | 
			
		||||
    public string $CodiceCUP = '';
 | 
			
		||||
    public string $CodiceCIG = '';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class DatiContratto extends Data{
 | 
			
		||||
    public string $RiferimentoNumeroLinea = '';
 | 
			
		||||
    public string $IdDocumento = '';
 | 
			
		||||
    public string $Data = '';
 | 
			
		||||
    public string $NumItem = '';
 | 
			
		||||
    public string $CodiceCommessaConvenzione = '';
 | 
			
		||||
    public string $CodiceCUP = '';
 | 
			
		||||
    public string $CodiceCIG = '';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class DatiRicezione extends Data{
 | 
			
		||||
    public string $RiferimentoNumeroLinea = '';
 | 
			
		||||
    public string $IdDocumento = '';
 | 
			
		||||
    public string $Data = '';
 | 
			
		||||
    public string $NumItem = '';
 | 
			
		||||
    public string $CodiceCommessaConvenzione = '';
 | 
			
		||||
    public string $CodiceCUP = '';
 | 
			
		||||
    public string $CodiceCIG = '';
 | 
			
		||||
}
 | 
			
		||||
@ -235,17 +235,17 @@ class Rule extends BaseRule implements RuleInterface
 | 
			
		||||
        } elseif(in_array($this->client_subregion, $this->eu_country_codes) && !$this->client->vat_number) { //eu country / no valid vat
 | 
			
		||||
            if(($this->client->company->tax_data->seller_subregion != $this->client_subregion) && $this->client->company->tax_data->regions->EU->has_sales_above_threshold) {
 | 
			
		||||
                // nlog("eu zone with sales above threshold");
 | 
			
		||||
                $this->tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->tax_rate;
 | 
			
		||||
                $this->reduced_tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->reduced_tax_rate;
 | 
			
		||||
                $this->tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->tax_rate ?? 0;
 | 
			
		||||
                $this->reduced_tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->reduced_tax_rate ?? 0;
 | 
			
		||||
            } else {
 | 
			
		||||
                // nlog("EU with intra-community supply ie DE to DE");
 | 
			
		||||
                $this->tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->tax_rate;
 | 
			
		||||
                $this->reduced_tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->reduced_tax_rate;
 | 
			
		||||
                $this->tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->tax_rate ?? 0;
 | 
			
		||||
                $this->reduced_tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->reduced_tax_rate ?? 0;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // nlog("default tax");
 | 
			
		||||
            $this->tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->tax_rate;
 | 
			
		||||
            $this->reduced_tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->reduced_tax_rate;
 | 
			
		||||
            $this->tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->tax_rate ?? 0;
 | 
			
		||||
            $this->reduced_tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->reduced_tax_rate ?? 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										103
									
								
								app/DataProviders/EDocRules.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								app/DataProviders/EDocRules.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,103 @@
 | 
			
		||||
<?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 EDocRules
 | 
			
		||||
{
 | 
			
		||||
    // [
 | 
			
		||||
    //     "key" => "",
 | 
			
		||||
    //     "label" => "",
 | 
			
		||||
    //     "type" => "dropdown/date/string/text",
 | 
			
		||||
    //     "resource" => "resource.json",
 | 
			
		||||
    //     "required" => true,
 | 
			
		||||
    // ]
 | 
			
		||||
    public function rules() 
 | 
			
		||||
    {
 | 
			
		||||
                
 | 
			
		||||
        return [
 | 
			
		||||
            'FatturaPA' => $this->FatturaPADefaults(),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private function FatturaPADefaults()
 | 
			
		||||
    {
 | 
			
		||||
        return [
 | 
			
		||||
            [
 | 
			
		||||
                "key" => "RegimeFiscale",
 | 
			
		||||
                "label" => "Regime Fiscale",
 | 
			
		||||
                "type" => "dropdown",
 | 
			
		||||
                "resource" => "RegimeFiscale.json",
 | 
			
		||||
                "required" => true,
 | 
			
		||||
            ],
 | 
			
		||||
            [
 | 
			
		||||
                "key" => "TipoDocumento",
 | 
			
		||||
                "label" => "Tipo Documento",
 | 
			
		||||
                "type" => "dropdown",
 | 
			
		||||
                "resource" => "TipoDocumento.json",
 | 
			
		||||
                "required" => true,
 | 
			
		||||
            ],
 | 
			
		||||
            [
 | 
			
		||||
                "key" => "ModalitaPagamento",
 | 
			
		||||
                "label" => "Modalita Pagamento",
 | 
			
		||||
                "type" => "dropdown",
 | 
			
		||||
                "resource" => "ModalitaPagamento.json",
 | 
			
		||||
                "required" => true,
 | 
			
		||||
            ],
 | 
			
		||||
            [
 | 
			
		||||
                "key" => "CondizioniPagamento",
 | 
			
		||||
                "label" => "Condizioni Pagamento",
 | 
			
		||||
                "type" => "dropdown",
 | 
			
		||||
                "resource" => "CondizioniPagamento.json",
 | 
			
		||||
                "required" => true,
 | 
			
		||||
            ],
 | 
			
		||||
            [
 | 
			
		||||
                "key" => "DatiRicezione",
 | 
			
		||||
                "label" => "Dati Ricezione",
 | 
			
		||||
                "type" => "dropdown",
 | 
			
		||||
                "resource" => "CondizioniPagamento",
 | 
			
		||||
                "required" => false,
 | 
			
		||||
                "children" => [],
 | 
			
		||||
            ],
 | 
			
		||||
            [
 | 
			
		||||
                "key" => "DatiContratto",
 | 
			
		||||
                "label" => "Dati Contratto",
 | 
			
		||||
                "type" => "object",
 | 
			
		||||
                "resource" => "DatiContratto",
 | 
			
		||||
                "required" => false,
 | 
			
		||||
                "children" => [
 | 
			
		||||
                    [
 | 
			
		||||
                        "key"=> "RiferimentoNumeroLinea",
 | 
			
		||||
                        "validation" => [
 | 
			
		||||
                            "string","min:1","max:10","required" 
 | 
			
		||||
                        ],
 | 
			
		||||
                    ]
 | 
			
		||||
                ],
 | 
			
		||||
            ],
 | 
			
		||||
            [
 | 
			
		||||
                "key" => "DatiOrdineAcquisto",
 | 
			
		||||
                "label" => "Dati Ordine Acquisto",
 | 
			
		||||
                "type" => "object",
 | 
			
		||||
                "resource" => "DatiOrdineAcquisto",
 | 
			
		||||
                "required" => false,
 | 
			
		||||
            ],
 | 
			
		||||
            [
 | 
			
		||||
                "key" => "DatiAnagraficiVettore",
 | 
			
		||||
                "label" => "Dati Anagrafici Vettore",
 | 
			
		||||
                "type" => "object",
 | 
			
		||||
                "resource" => "DatiAnagraficiVettore",
 | 
			
		||||
                "required" => false,
 | 
			
		||||
            ],
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -207,7 +207,6 @@ class InvoiceController extends Controller
 | 
			
		||||
 | 
			
		||||
        //format data
 | 
			
		||||
        $invoices->map(function ($invoice) {
 | 
			
		||||
            // $invoice->service()->removeUnpaidGatewayFees();
 | 
			
		||||
            $invoice->balance = $invoice->balance > 0 ? Number::formatValue($invoice->balance, $invoice->client->currency()) : 0;
 | 
			
		||||
            $invoice->partial = $invoice->partial > 0 ? Number::formatValue($invoice->partial, $invoice->client->currency()) : 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -144,7 +144,6 @@ class TwilioController extends BaseController
 | 
			
		||||
    public function generate2faResetCode(Generate2faRequest $request)
 | 
			
		||||
    {
 | 
			
		||||
        nlog($request->all());
 | 
			
		||||
        nlog($request->headers());
 | 
			
		||||
 | 
			
		||||
        $user = User::where('email', $request->email)->first();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -27,12 +27,18 @@ class StoreBankTransactionRuleRequest extends Request
 | 
			
		||||
     */
 | 
			
		||||
    public function authorize(): bool
 | 
			
		||||
    {
 | 
			
		||||
        return auth()->user()->can('create', BankTransactionRule::class) && auth()->user()->account->hasFeature(Account::FEATURE_API);
 | 
			
		||||
        /** @var \App\Models\User $user */
 | 
			
		||||
        $user = auth()->user();
 | 
			
		||||
 | 
			
		||||
        return $user->can('create', BankTransactionRule::class) && $user->account->hasFeature(Account::FEATURE_API);
 | 
			
		||||
        ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function rules()
 | 
			
		||||
    {
 | 
			
		||||
        /** @var \App\Models\User $user */
 | 
			
		||||
        $user = auth()->user();
 | 
			
		||||
 | 
			
		||||
        /* Ensure we have a client name, and that all emails are unique*/
 | 
			
		||||
        $rules = [
 | 
			
		||||
            'name' => 'bail|required|string',
 | 
			
		||||
@ -45,18 +51,9 @@ class StoreBankTransactionRuleRequest extends Request
 | 
			
		||||
            'applies_to' => 'bail|sometimes|string',
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        if (isset($this->category_id)) {
 | 
			
		||||
            $rules['category_id'] = 'bail|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isset($this->vendor_id)) {
 | 
			
		||||
            $rules['vendor_id'] = 'bail|sometimes|exists:vendors,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isset($this->client_id)) {
 | 
			
		||||
            $rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $rules['category_id'] = 'bail|sometimes|exists:expense_categories,id,company_id,'.$user->company()->id.',is_deleted,0';
 | 
			
		||||
        $rules['vendor_id'] = 'bail|sometimes|exists:vendors,id,company_id,'.$user->company()->id.',is_deleted,0';
 | 
			
		||||
        $rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.$user->company()->id.',is_deleted,0';
 | 
			
		||||
 | 
			
		||||
        return $rules;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -94,6 +94,10 @@ class StoreCreditRequest extends Request
 | 
			
		||||
            $input['design_id'] = $this->decodePrimaryKey($input['design_id']);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(isset($input['partial']) && $input['partial'] == 0) {
 | 
			
		||||
            $input['partial_due_date'] = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $input = $this->decodePrimaryKeys($input);
 | 
			
		||||
 | 
			
		||||
        $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
 | 
			
		||||
 | 
			
		||||
@ -86,6 +86,10 @@ class UpdateCreditRequest extends Request
 | 
			
		||||
 | 
			
		||||
        $input = $this->decodePrimaryKeys($input);
 | 
			
		||||
 | 
			
		||||
        if(isset($input['partial']) && $input['partial'] == 0) {
 | 
			
		||||
            $input['partial_due_date'] = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isset($input['line_items'])) {
 | 
			
		||||
            $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -93,8 +93,8 @@ class StoreInvoiceRequest extends Request
 | 
			
		||||
            $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(isset($input['partial']) && $input['partial'] == 0 && isset($input['partial_due_date'])) {
 | 
			
		||||
            $input['partial_due_date'] = '';
 | 
			
		||||
        if(isset($input['partial']) && $input['partial'] == 0) {
 | 
			
		||||
            $input['partial_due_date'] = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $input['amount'] = 0;
 | 
			
		||||
 | 
			
		||||
@ -91,8 +91,8 @@ class UpdateInvoiceRequest extends Request
 | 
			
		||||
 | 
			
		||||
        $input['id'] = $this->invoice->id;
 | 
			
		||||
 | 
			
		||||
        if(isset($input['partial']) && $input['partial'] == 0 && isset($input['partial_due_date'])) {
 | 
			
		||||
            $input['partial_due_date'] = '';
 | 
			
		||||
        if(isset($input['partial']) && $input['partial'] == 0) {
 | 
			
		||||
            $input['partial_due_date'] = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isset($input['line_items']) && is_array($input['line_items'])) {
 | 
			
		||||
 | 
			
		||||
@ -45,9 +45,9 @@ class UpdatePaymentRequest extends Request
 | 
			
		||||
            'client_id' => ['sometimes', 'bail', Rule::in([$this->payment->client_id])],
 | 
			
		||||
            'number' => ['sometimes', 'bail', Rule::unique('payments')->where('company_id', $user->company()->id)->ignore($this->payment->id)],
 | 
			
		||||
            'invoices' => ['sometimes', 'bail', 'nullable', 'array', new PaymentAppliedValidAmount($this->all())],
 | 
			
		||||
            'invoices.*.invoice_id' => ['sometimes','distinct',Rule::exists('invoices','id')->where('company_id', $user->company()->id)->where('client_id', $this->client_id)],
 | 
			
		||||
            'invoices.*.invoice_id' => ['sometimes','distinct',Rule::exists('invoices','id')->where('company_id', $user->company()->id)->where('client_id', $this->payment->client_id)],
 | 
			
		||||
            'invoices.*.amount' => ['sometimes','numeric','min:0'],
 | 
			
		||||
            'credits.*.credit_id' => ['sometimes','bail','distinct',Rule::exists('credits','id')->where('company_id', $user->company()->id)->where('client_id', $this->client_id)],
 | 
			
		||||
            'credits.*.credit_id' => ['sometimes','bail','distinct',Rule::exists('credits','id')->where('company_id', $user->company()->id)->where('client_id', $this->payment->client_id)],
 | 
			
		||||
            'credits.*.amount' => ['required', 'bail'],
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -79,6 +79,10 @@ class StorePurchaseOrderRequest extends Request
 | 
			
		||||
 | 
			
		||||
        $input = $this->decodePrimaryKeys($input);
 | 
			
		||||
 | 
			
		||||
        if(isset($input['partial']) && $input['partial'] == 0) {
 | 
			
		||||
            $input['partial_due_date'] = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isset($input['line_items']) && is_array($input['line_items'])) {
 | 
			
		||||
            $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -83,6 +83,10 @@ class UpdatePurchaseOrderRequest extends Request
 | 
			
		||||
 | 
			
		||||
        $input['id'] = $this->purchase_order->id;
 | 
			
		||||
 | 
			
		||||
        if(isset($input['partial']) && $input['partial'] == 0) {
 | 
			
		||||
            $input['partial_due_date'] = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isset($input['line_items']) && is_array($input['line_items'])) {
 | 
			
		||||
            $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -43,7 +43,7 @@ class StoreQuoteRequest extends Request
 | 
			
		||||
 | 
			
		||||
        $rules = [];
 | 
			
		||||
 | 
			
		||||
        $rules['client_id'] = 'required|exists:clients,id,company_id,'.$user->company()->id;
 | 
			
		||||
        $rules['client_id'] = ['required', 'bail', Rule::exists('clients','id')->where('company_id', $user->company()->id)];
 | 
			
		||||
 | 
			
		||||
        if ($this->file('documents') && is_array($this->file('documents'))) {
 | 
			
		||||
            $rules['documents.*'] = $this->fileValidation();
 | 
			
		||||
@ -64,12 +64,17 @@ class StoreQuoteRequest extends Request
 | 
			
		||||
        $rules['is_amount_discount'] = ['boolean'];
 | 
			
		||||
        $rules['exchange_rate'] = 'bail|sometimes|numeric';
 | 
			
		||||
        $rules['line_items'] = 'array';
 | 
			
		||||
        $rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date', 'after_or_equal:date'];
 | 
			
		||||
        $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
 | 
			
		||||
 | 
			
		||||
        return $rules;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function prepareForValidation()
 | 
			
		||||
    {
 | 
			
		||||
        /** @var \App\Models\User $user */
 | 
			
		||||
        $user = auth()->user();
 | 
			
		||||
        
 | 
			
		||||
        $input = $this->all();
 | 
			
		||||
 | 
			
		||||
        $input = $this->decodePrimaryKeys($input);
 | 
			
		||||
@ -82,6 +87,19 @@ class StoreQuoteRequest extends Request
 | 
			
		||||
            $input['exchange_rate'] = 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(isset($input['partial']) && $input['partial'] == 0) {
 | 
			
		||||
            $input['partial_due_date'] = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(!isset($input['date']))
 | 
			
		||||
            $input['date'] = now()->addSeconds($user->company()->utc_offset())->format('Y-m-d');
 | 
			
		||||
 | 
			
		||||
        if(isset($input['partial_due_date']) && (!isset($input['due_date']) || strlen($input['due_date']) <=1 )) {
 | 
			
		||||
            $client = \App\Models\Client::withTrashed()->find($input['client_id']);
 | 
			
		||||
            $valid_days = ($client && strlen($client->getSetting('valid_until')) >= 1) ? $client->getSetting('valid_until') : 7; 
 | 
			
		||||
            $input['due_date'] = \Carbon\Carbon::parse($input['date'])->addDays($valid_days)->format('Y-m-d');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->replace($input);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -56,16 +56,16 @@ class UpdateQuoteRequest extends Request
 | 
			
		||||
            $rules['file'] = $this->fileValidation();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $rules['number'] = ['bail', 'sometimes', 'nullable', Rule::unique('quotes')->where('company_id', $user->company()->id)->ignore($this->quote->id)];
 | 
			
		||||
 | 
			
		||||
        $rules['client_id'] = ['bail', 'sometimes', Rule::in([$this->quote->client_id])];
 | 
			
		||||
 | 
			
		||||
        $rules['line_items'] = 'array';
 | 
			
		||||
        $rules['discount'] = 'sometimes|numeric';
 | 
			
		||||
        $rules['is_amount_discount'] = ['boolean'];
 | 
			
		||||
        $rules['exchange_rate'] = 'bail|sometimes|numeric';
 | 
			
		||||
 | 
			
		||||
        $rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date'];
 | 
			
		||||
        $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', 'after_or_equal:date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
 | 
			
		||||
 | 
			
		||||
        return $rules;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -89,6 +89,9 @@ class UpdateQuoteRequest extends Request
 | 
			
		||||
            $input['exchange_rate'] = 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(isset($input['partial']) && $input['partial'] == 0) {
 | 
			
		||||
            $input['partial_due_date'] = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->replace($input);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -31,14 +31,6 @@ class StoreSetupRequest extends Request
 | 
			
		||||
            /*System*/
 | 
			
		||||
            'url'              => 'required',
 | 
			
		||||
            /*Mail driver*/
 | 
			
		||||
            'mail_driver'      => 'required',
 | 
			
		||||
            'encryption'       => 'required_unless:mail_driver,log',
 | 
			
		||||
            'mail_host'        => 'required_unless:mail_driver,log',
 | 
			
		||||
            'mail_username'    => 'required_unless:mail_driver,log',
 | 
			
		||||
            'mail_name'        => 'required_unless:mail_driver,log',
 | 
			
		||||
            'mail_address'     => 'required_unless:mail_driver,log',
 | 
			
		||||
            'mail_password'    => 'required_unless:mail_driver,log',
 | 
			
		||||
            /*user registration*/
 | 
			
		||||
            'privacy_policy'   => 'required',
 | 
			
		||||
            'terms_of_service' => 'required',
 | 
			
		||||
            'first_name'       => 'required',
 | 
			
		||||
 | 
			
		||||
@ -137,6 +137,9 @@ class UpdateTaskRequest extends Request
 | 
			
		||||
            $input['time_log'] = json_encode([]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(isset($input['user']))
 | 
			
		||||
            unset($input['user']);
 | 
			
		||||
 | 
			
		||||
        $this->replace($input);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -255,6 +255,9 @@ class BaseImport
 | 
			
		||||
 | 
			
		||||
            unset($record['']);
 | 
			
		||||
 | 
			
		||||
            if(!is_array($record))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                $entity = $this->transformer->transform($record);
 | 
			
		||||
 | 
			
		||||
@ -310,6 +313,11 @@ class BaseImport
 | 
			
		||||
        $count = 0;
 | 
			
		||||
 | 
			
		||||
        foreach ($data as $key => $record) {
 | 
			
		||||
            
 | 
			
		||||
            if(!is_array($record)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                $entity = $this->transformer->transform($record);
 | 
			
		||||
                $validator = $this->request_name::runFormRequest($entity);
 | 
			
		||||
@ -372,6 +380,11 @@ class BaseImport
 | 
			
		||||
        $invoices = $this->groupInvoices($invoices, $invoice_number_key);
 | 
			
		||||
 | 
			
		||||
        foreach ($invoices as $raw_invoice) {
 | 
			
		||||
            
 | 
			
		||||
            if(!is_array($raw_invoice)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                $invoice_data = $invoice_transformer->transform($raw_invoice);
 | 
			
		||||
 | 
			
		||||
@ -459,6 +472,11 @@ class BaseImport
 | 
			
		||||
 | 
			
		||||
        foreach ($tasks as $raw_task) {
 | 
			
		||||
            $task_data = [];
 | 
			
		||||
            
 | 
			
		||||
            if(!is_array($raw_task)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                $task_data = $task_transformer->transform($raw_task);
 | 
			
		||||
                $task_data['user_id'] = $this->company->owner()->id;
 | 
			
		||||
@ -527,6 +545,11 @@ class BaseImport
 | 
			
		||||
        $invoices = $this->groupInvoices($invoices, $invoice_number_key);
 | 
			
		||||
 | 
			
		||||
        foreach ($invoices as $raw_invoice) {
 | 
			
		||||
            
 | 
			
		||||
            if(!is_array($raw_invoice)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                $invoice_data = $invoice_transformer->transform($raw_invoice);
 | 
			
		||||
                $invoice_data['user_id'] = $this->company->owner()->id;
 | 
			
		||||
@ -742,6 +765,11 @@ class BaseImport
 | 
			
		||||
        $quotes = $this->groupInvoices($quotes, $quote_number_key);
 | 
			
		||||
 | 
			
		||||
        foreach ($quotes as $raw_quote) {
 | 
			
		||||
            
 | 
			
		||||
            if(!is_array($raw_quote)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                $quote_data = $quote_transformer->transform($raw_quote);
 | 
			
		||||
                $quote_data['line_items'] = $this->cleanItems(
 | 
			
		||||
 | 
			
		||||
@ -232,6 +232,11 @@ class Wave extends BaseImport implements ImportInterface
 | 
			
		||||
        $expenses = $this->groupExpenses($data);
 | 
			
		||||
 | 
			
		||||
        foreach ($expenses as $raw_expense) {
 | 
			
		||||
                    
 | 
			
		||||
            if(!is_array($raw_expense)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                $expense_data = $expense_transformer->transform($raw_expense);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -115,7 +115,7 @@ class BaseTransformer
 | 
			
		||||
        return isset($data[$field]) && $data[$field] ? $data[$field] : null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getCurrencyByCode($data, $key = 'client.currency_id')
 | 
			
		||||
    public function getCurrencyByCode(array $data, string $key = 'client.currency_id')
 | 
			
		||||
    {
 | 
			
		||||
        $code = array_key_exists($key, $data) ? $data[$key] : false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,7 @@ use Illuminate\Foundation\Events\Dispatchable;
 | 
			
		||||
use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
use Illuminate\Support\Carbon;
 | 
			
		||||
 | 
			
		||||
class ProcessBankTransactionsYodlee implements ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
@ -71,8 +72,8 @@ class ProcessBankTransactionsYodlee implements ShouldQueue
 | 
			
		||||
 | 
			
		||||
        set_time_limit(0);
 | 
			
		||||
 | 
			
		||||
        //Loop through everything until we are up to date
 | 
			
		||||
        $this->from_date = $this->from_date ?: '2021-01-01';
 | 
			
		||||
        //Loop through everything until we are up to date - improve handling of delayed accounts
 | 
			
		||||
        $this->from_date = $this->from_date ? Carbon::parse($this->from_date)->subWeeks(2)->format('Y-m-d') : '2021-01-01';
 | 
			
		||||
 | 
			
		||||
        nlog("Yodlee: Processing transactions for account: {$this->bank_integration->account->key}");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -61,7 +61,8 @@ class ClientLedgerBalanceUpdate implements ShouldQueue
 | 
			
		||||
                                                    ->where('id', '<', $company_ledger->id)
 | 
			
		||||
                                                    ->where('client_id', $company_ledger->client_id)
 | 
			
		||||
                                                    ->where('company_id', $company_ledger->company_id)
 | 
			
		||||
                                                    ->where('balance', '!=', 0)
 | 
			
		||||
                                                    ->whereNotNull('balance')
 | 
			
		||||
                                                    // ->where('balance', '!=', 0)
 | 
			
		||||
                                                    ->orderBy('id', 'DESC')
 | 
			
		||||
                                                    ->first();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -23,31 +23,6 @@ class Submit extends Component
 | 
			
		||||
    public function mount()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        // $request = new \Illuminate\Http\Request([
 | 
			
		||||
        //     'sidebar' => 'hidden',
 | 
			
		||||
        //     'hash' => $this->context['hash'],
 | 
			
		||||
        //     'action' => 'payment',
 | 
			
		||||
        //     'invoices' => [
 | 
			
		||||
        //         $this->context['form']['invoice_hashed_id'],
 | 
			
		||||
        //     ],
 | 
			
		||||
        //     'payable_invoices' => [
 | 
			
		||||
        //         [
 | 
			
		||||
        //         'amount' => $this->context['form']['payable_amount'],
 | 
			
		||||
        //         'invoice_id' => $this->context['form']['invoice_hashed_id'],
 | 
			
		||||
        //         ],
 | 
			
		||||
        //     ],
 | 
			
		||||
        //     'company_gateway_id' => $this->context['form']['company_gateway_id'],
 | 
			
		||||
        //     'payment_method_id' => $this->context['form']['payment_method_id'],
 | 
			
		||||
        //     'contact_first_name' => $this->context['contact']['first_name'],
 | 
			
		||||
        //     'contact_last_name' => $this->context['contact']['last_name'],
 | 
			
		||||
        //     'contact_email' => $this->context['contact']['email'],
 | 
			
		||||
        // ]);
 | 
			
		||||
 | 
			
		||||
        // return redirect((new InstantPayment($request))->run());
 | 
			
		||||
            // dd($this->context);
 | 
			
		||||
 | 
			
		||||
            nlog($this->context);
 | 
			
		||||
 | 
			
		||||
        $this->dispatch(
 | 
			
		||||
            'purchase.submit',
 | 
			
		||||
            invoice_hashed_id: $this->context['form']['invoice_hashed_id'],
 | 
			
		||||
 | 
			
		||||
@ -77,7 +77,7 @@ use Laracasts\Presenter\PresentableTrait;
 | 
			
		||||
 * @property float $amount
 | 
			
		||||
 * @property float $balance
 | 
			
		||||
 * @property float|null $partial
 | 
			
		||||
 * @property string|null $partial_due_date
 | 
			
		||||
 * @property \Carbon\Carbon|null $partial_due_date
 | 
			
		||||
 * @property string|null $last_viewed
 | 
			
		||||
 * @property int|null $created_at
 | 
			
		||||
 * @property int|null $updated_at
 | 
			
		||||
@ -195,10 +195,10 @@ class Quote extends BaseModel
 | 
			
		||||
        return $this->dateMutator($value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getDueDateAttribute($value)
 | 
			
		||||
    {
 | 
			
		||||
        return $value ? $this->dateMutator($value) : null;
 | 
			
		||||
    }
 | 
			
		||||
//    public function getDueDateAttribute($value)
 | 
			
		||||
//    {
 | 
			
		||||
//        return $value ? $this->dateMutator($value) : null;
 | 
			
		||||
//    }
 | 
			
		||||
 | 
			
		||||
    // public function getPartialDueDateAttribute($value)
 | 
			
		||||
    // {
 | 
			
		||||
 | 
			
		||||
@ -166,6 +166,15 @@ class Vendor extends BaseModel
 | 
			
		||||
        return $this->hasMany(Activity::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getCurrencyCode(): string
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->currency()) {
 | 
			
		||||
            return $this->currency()->code;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return 'USD';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function currency()
 | 
			
		||||
    {
 | 
			
		||||
        $currencies = Cache::get('currencies');
 | 
			
		||||
 | 
			
		||||
@ -166,7 +166,10 @@ class PayPalRestPaymentDriver extends BaseDriver
 | 
			
		||||
        $data['gateway_type_id'] = $this->gateway_type_id;
 | 
			
		||||
        $data['currency'] = $this->client->currency()->code;
 | 
			
		||||
 | 
			
		||||
        return render('gateways.paypal.pay', $data);
 | 
			
		||||
 | 
			
		||||
// return render('gateways.paypal.ppcp.card', $data);
 | 
			
		||||
 | 
			
		||||
return render('gateways.paypal.pay', $data);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -324,23 +327,43 @@ class PayPalRestPaymentDriver extends BaseDriver
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function createOrder(array $data): string
 | 
			
		||||
    private function getPaymentSource(): array
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $_invoice = collect($this->payment_hash->data->invoices)->first();
 | 
			
		||||
        if($this->gateway_type_id == 1) {
 | 
			
		||||
 | 
			
		||||
        $invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id));
 | 
			
		||||
            return [
 | 
			
		||||
                "card" => [
 | 
			
		||||
                    "attributes" => [
 | 
			
		||||
                        "verification" => [
 | 
			
		||||
                            "method" => "SCA_WHEN_REQUIRED", //SCA_ALWAYS
 | 
			
		||||
                        ],
 | 
			
		||||
                    ],
 | 
			
		||||
                    "name" => $this->client->present()->primary_contact_name(),
 | 
			
		||||
                    "email_address" => $this->client->present()->email(),
 | 
			
		||||
                    "address" => [
 | 
			
		||||
                        "address_line_1" => $this->client->address1,
 | 
			
		||||
                        "address_line_2" => $this->client->address2,
 | 
			
		||||
                        "admin_area_2" => $this->client->city,
 | 
			
		||||
                        "admin_area_1" => $this->client->state,
 | 
			
		||||
                        "postal_code" => $this->client->postal_code,
 | 
			
		||||
                        "country_code" => $this->client->country->iso_3166_2,
 | 
			
		||||
                    ],
 | 
			
		||||
                    "experience_context" => [
 | 
			
		||||
                        "user_action" => "PAY_NOW"
 | 
			
		||||
                    ],
 | 
			
		||||
                    "stored_credential" => [
 | 
			
		||||
                        "payment_initiator" => "MERCHANT", //"CUSTOMER" who initiated the transaction?
 | 
			
		||||
                        "payment_type" => "UNSCHEDULED",
 | 
			
		||||
                        "usage"=> "DERIVED",
 | 
			
		||||
                    ],
 | 
			
		||||
                ],
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
        $description = collect($invoice->line_items)->map(function ($item) {
 | 
			
		||||
            return $item->notes;
 | 
			
		||||
        })->implode("\n");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $order = [
 | 
			
		||||
 | 
			
		||||
                "intent" => "CAPTURE",
 | 
			
		||||
                "payment_source" => [
 | 
			
		||||
        return [
 | 
			
		||||
            "paypal" => [
 | 
			
		||||
 | 
			
		||||
                "name" => [
 | 
			
		||||
                    "given_name" => $this->client->present()->first_name(),
 | 
			
		||||
                    "surname" => $this->client->present()->last_name(),
 | 
			
		||||
@ -358,7 +381,26 @@ class PayPalRestPaymentDriver extends BaseDriver
 | 
			
		||||
                    "user_action" => "PAY_NOW"
 | 
			
		||||
                ],
 | 
			
		||||
            ],
 | 
			
		||||
                ],
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private function createOrder(array $data): string
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $_invoice = collect($this->payment_hash->data->invoices)->first();
 | 
			
		||||
 | 
			
		||||
        $invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id));
 | 
			
		||||
 | 
			
		||||
        $description = collect($invoice->line_items)->map(function ($item) {
 | 
			
		||||
            return $item->notes;
 | 
			
		||||
        })->implode("\n");
 | 
			
		||||
 | 
			
		||||
        $order = [
 | 
			
		||||
 | 
			
		||||
                "intent" => "CAPTURE",
 | 
			
		||||
                "payment_source" => $this->getPaymentSource(),
 | 
			
		||||
                "purchase_units" => [
 | 
			
		||||
                    [
 | 
			
		||||
                    "custom_id" => $this->payment_hash->hash,
 | 
			
		||||
 | 
			
		||||
@ -111,7 +111,7 @@ class QuoteTransformer extends EntityTransformer
 | 
			
		||||
            'reminder2_sent' => $quote->reminder2_sent ?: '',
 | 
			
		||||
            'reminder3_sent' => $quote->reminder3_sent ?: '',
 | 
			
		||||
            'reminder_last_sent' => $quote->reminder_last_sent ?: '',
 | 
			
		||||
            'due_date' => $quote->due_date ?: '',
 | 
			
		||||
            'due_date' => $quote->due_date ? $quote->due_date->format('Y-m-d') : '',
 | 
			
		||||
            'terms' => $quote->terms ?: '',
 | 
			
		||||
            'public_notes' => $quote->public_notes ?: '',
 | 
			
		||||
            'private_notes' => $quote->private_notes ?: '',
 | 
			
		||||
@ -127,7 +127,7 @@ class QuoteTransformer extends EntityTransformer
 | 
			
		||||
            'is_amount_discount' => (bool) ($quote->is_amount_discount ?: false),
 | 
			
		||||
            'footer' => $quote->footer ?: '',
 | 
			
		||||
            'partial' => (float) ($quote->partial ?: 0.0),
 | 
			
		||||
            'partial_due_date' => $quote->partial_due_date ?: '',
 | 
			
		||||
            'partial_due_date' => $quote->partial_due_date ? $quote->partial_due_date->format('Y-m-d') : '',
 | 
			
		||||
            'custom_value1' => (string) $quote->custom_value1 ?: '',
 | 
			
		||||
            'custom_value2' => (string) $quote->custom_value2 ?: '',
 | 
			
		||||
            'custom_value3' => (string) $quote->custom_value3 ?: '',
 | 
			
		||||
 | 
			
		||||
@ -101,6 +101,12 @@ class Number
 | 
			
		||||
        if($comma === false) //no comma must be a decimal number already
 | 
			
		||||
            return (float) $value;
 | 
			
		||||
 | 
			
		||||
        if(!$decimal && substr($value, -3, 1) != ","){
 | 
			
		||||
            $value = $value.".00";
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        $decimal = strpos($value, '.');
 | 
			
		||||
 | 
			
		||||
        if($decimal < $comma){ //decimal before a comma = euro
 | 
			
		||||
            $value = str_replace(['.',','], ['','.'], $value);
 | 
			
		||||
            return (float) $value;
 | 
			
		||||
@ -113,6 +119,7 @@ class Number
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Formats a given value based on the clients currency
 | 
			
		||||
     * BACK to a float.
 | 
			
		||||
@ -228,9 +235,6 @@ class Number
 | 
			
		||||
        $code = $currency->code;
 | 
			
		||||
        $swapSymbol = $currency->swap_currency_symbol;
 | 
			
		||||
 | 
			
		||||
        // App\Models\Client::country() returns instance of BelongsTo.
 | 
			
		||||
        // App\Models\Company::country() returns record for the country, that's why we check for the instance.
 | 
			
		||||
 | 
			
		||||
        if ($entity instanceof Company) {
 | 
			
		||||
            $country = $entity->country();
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
@ -26,9 +26,7 @@ trait SubscriptionHooker
 | 
			
		||||
            'X-Requested-With' => 'XMLHttpRequest',
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $post_purchase_rest_method = &$subscription->webhook_configuration['post_purchase_rest_method'];
 | 
			
		||||
 | 
			
		||||
        if (!isset($subscription->webhook_configuration['post_purchase_url']) && !$post_purchase_rest_method) {
 | 
			
		||||
        if (!isset($subscription->webhook_configuration['post_purchase_url']) && !isset($subscription->webhook_configuration['post_purchase_rest_method'])) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -17,8 +17,8 @@ return [
 | 
			
		||||
    'require_https' => env('REQUIRE_HTTPS', true),
 | 
			
		||||
    'app_url' => rtrim(env('APP_URL', ''), '/'),
 | 
			
		||||
    'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
 | 
			
		||||
    'app_version' => env('APP_VERSION', '5.8.48'),
 | 
			
		||||
    'app_tag' => env('APP_TAG', '5.8.48'),
 | 
			
		||||
    'app_version' => env('APP_VERSION', '5.8.49'),
 | 
			
		||||
    'app_tag' => env('APP_TAG', '5.8.49'),
 | 
			
		||||
    'minimum_client_version' => '5.0.16',
 | 
			
		||||
    'terms_version' => '1.0.1',
 | 
			
		||||
    'api_secret' => env('API_SECRET', false),
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,183 @@
 | 
			
		||||
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title' => ''])
 | 
			
		||||
 | 
			
		||||
@section('gateway_head')
 | 
			
		||||
 | 
			
		||||
@endsection
 | 
			
		||||
 | 
			
		||||
@section('gateway_content')
 | 
			
		||||
    <form action="{{ route('client.payments.response') }}" method="post" id="server_response">
 | 
			
		||||
        @csrf
 | 
			
		||||
        <input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
 | 
			
		||||
        <input type="hidden" name="company_gateway_id" value="{{ $gateway->company_gateway->id }}">
 | 
			
		||||
        <input type="hidden" name="gateway_type_id" id="gateway_type_id" value="{{ $gateway_type_id }}">
 | 
			
		||||
        <input type="hidden" name="gateway_response" id="gateway_response">
 | 
			
		||||
        <input type="hidden" name="amount_with_fee" id="amount_with_fee" value="{{ $total['amount_with_fee'] }}"/>
 | 
			
		||||
    </form>
 | 
			
		||||
 | 
			
		||||
  <div class="alert alert-failure mb-4" hidden id="errors"></div>
 | 
			
		||||
 | 
			
		||||
   <div id="paypal-button-container" class="paypal-button-container"></div>
 | 
			
		||||
 | 
			
		||||
    <div id="checkout-form">
 | 
			
		||||
      <!-- Containers for Card Fields hosted by PayPal -->
 | 
			
		||||
      <!-- <div id="card-name-field-container"></div> -->
 | 
			
		||||
      <div id="card-number-field-container"></div>
 | 
			
		||||
      <div class="expcvv" style="display:flex;">
 | 
			
		||||
        <div id="card-expiry-field-container" style="width:50%"></div>
 | 
			
		||||
        <div id="card-cvv-field-container" style="width:50%"></div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <!-- <button id="card-field-submit-button" type="button">
 | 
			
		||||
        {{ ctrans('texts.pay_now') }}
 | 
			
		||||
      </button> -->
 | 
			
		||||
      @include('portal.ninja2020.gateways.includes.pay_now')
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
@endsection
 | 
			
		||||
 | 
			
		||||
@section('gateway_footer')
 | 
			
		||||
@endsection
 | 
			
		||||
 | 
			
		||||
@push('footer')
 | 
			
		||||
<link  rel="stylesheet" type="text/css" href=https://www.paypalobjects.com/webstatic/en_US/developer/docs/css/cardfields.css />
 | 
			
		||||
<script src="https://www.paypal.com/sdk/js?client-id={!! $client_id !!}&components=card-fields"  data-partner-attribution-id="invoiceninja_SP_PPCP"></script>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
 | 
			
		||||
    const clientId = "{{ $client_id }}";
 | 
			
		||||
    const orderId = "{!! $order_id !!}";
 | 
			
		||||
 | 
			
		||||
    const cardStyle = {
 | 
			
		||||
        'input': {
 | 
			
		||||
            'font-size': '16px',
 | 
			
		||||
            'font-family': 'courier, monospace',
 | 
			
		||||
            'font-weight': 'lighter',
 | 
			
		||||
            'color': '#ccc',
 | 
			
		||||
        },
 | 
			
		||||
        '.invalid': {
 | 
			
		||||
            'color': 'purple',
 | 
			
		||||
        },
 | 
			
		||||
        '.expcvv': {
 | 
			
		||||
          'display': 'grid',
 | 
			
		||||
          'grid-template-columns': 'auto'
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const cardField = paypal.CardFields({
 | 
			
		||||
        // style: cardStyle,
 | 
			
		||||
        client: clientId,
 | 
			
		||||
        createOrder: function(data, actions) {
 | 
			
		||||
            return orderId;  
 | 
			
		||||
        },
 | 
			
		||||
        onApprove: function(data, actions) {
 | 
			
		||||
 | 
			
		||||
            var errorDetail = Array.isArray(data.details) && data.details[0];
 | 
			
		||||
                if (errorDetail && ['INSTRUMENT_DECLINED', 'PAYER_ACTION_REQUIRED'].includes(errorDetail.issue)) {
 | 
			
		||||
                return actions.restart();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            console.log("on approve");
 | 
			
		||||
            console.log(data);
 | 
			
		||||
            console.log(actions);
 | 
			
		||||
 | 
			
		||||
            document.getElementById("gateway_response").value = JSON.stringify( data );
 | 
			
		||||
            document.getElementById("server_response").submit();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        },
 | 
			
		||||
        onCancel: function() {
 | 
			
		||||
 | 
			
		||||
            window.location.href = "/client/invoices/";
 | 
			
		||||
        },
 | 
			
		||||
        // onError: function(error) {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //     document.getElementById('errors').textContent = `Sorry, your transaction could not be processed...\n\n${error.message}`;
 | 
			
		||||
        //     document.getElementById('errors').hidden = false;
 | 
			
		||||
 | 
			
		||||
        //     // document.getElementById("gateway_response").value = error;
 | 
			
		||||
        //     // document.getElementById("server_response").submit();
 | 
			
		||||
        // },
 | 
			
		||||
        onClick: function (){
 | 
			
		||||
           
 | 
			
		||||
        }
 | 
			
		||||
    
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
  // Render each field after checking for eligibility
 | 
			
		||||
  if (cardField.isEligible()) {
 | 
			
		||||
 | 
			
		||||
      const numberField = cardField.NumberField({
 | 
			
		||||
        inputEvents: {
 | 
			
		||||
            onChange: (event)=> {
 | 
			
		||||
                console.log("returns a stateObject", event);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
      numberField.render("#card-number-field-container");
 | 
			
		||||
 | 
			
		||||
      const cvvField = cardField.CVVField({
 | 
			
		||||
        inputEvents: {
 | 
			
		||||
            onChange: (event)=> {
 | 
			
		||||
                console.log("returns a stateObject", event);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
      cvvField.render("#card-cvv-field-container");
 | 
			
		||||
 | 
			
		||||
      const expiryField = cardField.ExpiryField({
 | 
			
		||||
        inputEvents: {
 | 
			
		||||
            onChange: (event)=> {
 | 
			
		||||
                console.log("returns a stateObject", event);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
      expiryField.render("#card-expiry-field-container");
 | 
			
		||||
 | 
			
		||||
      document.getElementById("pay-now").addEventListener('click', (e) => {
 | 
			
		||||
 | 
			
		||||
        document.getElementById('errors').textContent = '';
 | 
			
		||||
        document.getElementById('errors').hidden = true;
 | 
			
		||||
        
 | 
			
		||||
        document.getElementById('pay-now').disabled = true;
 | 
			
		||||
        document.querySelector('#pay-now > svg').classList.remove('hidden');
 | 
			
		||||
        document.querySelector('#pay-now > svg').classList.add('justify-center');
 | 
			
		||||
        document.querySelector('#pay-now > span').classList.add('hidden');
 | 
			
		||||
 | 
			
		||||
        cardField.submit().then((response) => {
 | 
			
		||||
          console.log("then");
 | 
			
		||||
          console.log(response);
 | 
			
		||||
          // lets goooo
 | 
			
		||||
 | 
			
		||||
        }).catch((error) => {
 | 
			
		||||
 | 
			
		||||
            console.log(error);
 | 
			
		||||
 | 
			
		||||
            document.getElementById('pay-now').disabled = false;
 | 
			
		||||
            document.querySelector('#pay-now > svg').classList.add('hidden');
 | 
			
		||||
            document.querySelector('#pay-now > span').classList.remove('hidden');
 | 
			
		||||
            
 | 
			
		||||
            if(error.message == 'INVALID_NUMBER'){
 | 
			
		||||
              document.getElementById('errors').textContent = "{{ ctrans('texts.invalid_card_number') }}";
 | 
			
		||||
            }
 | 
			
		||||
            else if(error.message == 'INVALID_CVV') {
 | 
			
		||||
              document.getElementById('errors').textContent = "{{ ctrans('texts.invalid_cvv') }}";
 | 
			
		||||
            }
 | 
			
		||||
            else if(error.message == 'INVALID_EXPIRY') {
 | 
			
		||||
              document.getElementById('errors').textContent = "{{ ctrans('texts.invalid_cvv') }}";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            document.getElementById('errors').hidden = false;
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
  else {
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
@endpush
 | 
			
		||||
@ -12,16 +12,17 @@
 | 
			
		||||
 | 
			
		||||
namespace Tests\Feature\Bank;
 | 
			
		||||
 | 
			
		||||
use App\Factory\BankIntegrationFactory;
 | 
			
		||||
use App\Factory\BankTransactionFactory;
 | 
			
		||||
use App\Factory\InvoiceFactory;
 | 
			
		||||
use App\Factory\InvoiceItemFactory;
 | 
			
		||||
use App\Models\BankTransaction;
 | 
			
		||||
use Tests\TestCase;
 | 
			
		||||
use App\Models\Expense;
 | 
			
		||||
use App\Models\Invoice;
 | 
			
		||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
 | 
			
		||||
use Tests\MockAccountData;
 | 
			
		||||
use Tests\TestCase;
 | 
			
		||||
use App\Factory\InvoiceFactory;
 | 
			
		||||
use App\Models\BankTransaction;
 | 
			
		||||
use App\Factory\InvoiceItemFactory;
 | 
			
		||||
use App\Factory\BankIntegrationFactory;
 | 
			
		||||
use App\Factory\BankTransactionFactory;
 | 
			
		||||
use Illuminate\Routing\Middleware\ThrottleRequests;
 | 
			
		||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
 | 
			
		||||
 | 
			
		||||
class BankTransactionTest extends TestCase
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -54,12 +54,99 @@ class QuoteTest extends TestCase
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testQuoteDueDateInjectionValidationLayer()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $data = [
 | 
			
		||||
            'client_id' => $this->client->hashed_id,
 | 
			
		||||
            'partial_due_date' => now()->format('Y-m-d'),
 | 
			
		||||
            'partial' => 1,
 | 
			
		||||
            'amount' => 20,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
                    'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
                    'X-API-TOKEN' => $this->token,
 | 
			
		||||
                ])->postJson('/api/v1/quotes', $data);
 | 
			
		||||
        
 | 
			
		||||
        $arr = $response->json();
 | 
			
		||||
        // nlog($arr);
 | 
			
		||||
 | 
			
		||||
        $this->assertNotEmpty($arr['data']['due_date']);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testNullDueDates()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $data = [
 | 
			
		||||
            'client_id' => $this->client->hashed_id,
 | 
			
		||||
            'due_date' => '',
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
                    'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
                    'X-API-TOKEN' => $this->token,
 | 
			
		||||
                ])->postJson('/api/v1/quotes', $data);
 | 
			
		||||
 | 
			
		||||
        $response->assertStatus(200);
 | 
			
		||||
 | 
			
		||||
        $arr = $response->json();
 | 
			
		||||
 | 
			
		||||
        $this->assertEmpty($arr['data']['due_date']);
 | 
			
		||||
        
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
                            'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
                            'X-API-TOKEN' => $this->token,
 | 
			
		||||
                        ])->putJson('/api/v1/quotes/'.$arr['data']['id'], $arr['data']);
 | 
			
		||||
 | 
			
		||||
        $response->assertStatus(200);
 | 
			
		||||
 | 
			
		||||
        $arr = $response->json();
 | 
			
		||||
 | 
			
		||||
        $this->assertEmpty($arr['data']['due_date']);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function testNonNullDueDates()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $data = [
 | 
			
		||||
            'client_id' => $this->client->hashed_id,
 | 
			
		||||
            'due_date' => now()->addDays(10),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
                    'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
                    'X-API-TOKEN' => $this->token,
 | 
			
		||||
                ])->postJson('/api/v1/quotes', $data);
 | 
			
		||||
 | 
			
		||||
        $response->assertStatus(200);
 | 
			
		||||
 | 
			
		||||
        $arr = $response->json();
 | 
			
		||||
 | 
			
		||||
        $this->assertNotEmpty($arr['data']['due_date']);
 | 
			
		||||
        
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
                            'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
                            'X-API-TOKEN' => $this->token,
 | 
			
		||||
                        ])->putJson('/api/v1/quotes/'.$arr['data']['id'], $arr['data']);
 | 
			
		||||
 | 
			
		||||
        $response->assertStatus(200);
 | 
			
		||||
 | 
			
		||||
        $arr = $response->json();
 | 
			
		||||
 | 
			
		||||
        $this->assertNotEmpty($arr['data']['due_date']);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testPartialDueDates()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $data = [
 | 
			
		||||
            'client_id' => $this->client->hashed_id,
 | 
			
		||||
            'due_date' => now()->format('Y-m-d'),
 | 
			
		||||
            'due_date' => now()->addDay()->format('Y-m-d'),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
@ -73,6 +160,41 @@ class QuoteTest extends TestCase
 | 
			
		||||
 | 
			
		||||
        $this->assertNotNull($arr['data']['due_date']);
 | 
			
		||||
        $this->assertEmpty($arr['data']['partial_due_date']);
 | 
			
		||||
 | 
			
		||||
        $data = [
 | 
			
		||||
            'client_id' => $this->client->hashed_id,
 | 
			
		||||
            'due_date' => now()->addDay()->format('Y-m-d'),
 | 
			
		||||
            'partial' => 1,
 | 
			
		||||
            'partial_due_date' => now()->format('Y-m-d'),
 | 
			
		||||
            'amount' => 20,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
                    'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
                    'X-API-TOKEN' => $this->token,
 | 
			
		||||
                ])->postJson('/api/v1/quotes', $data);
 | 
			
		||||
 | 
			
		||||
        $response->assertStatus(200);
 | 
			
		||||
 | 
			
		||||
        $arr = $response->json();
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals(now()->addDay()->format('Y-m-d'), $arr['data']['due_date']);
 | 
			
		||||
        $this->assertEquals(now()->format('Y-m-d'), $arr['data']['partial_due_date']);
 | 
			
		||||
        $this->assertEquals(1, $arr['data']['partial']);
 | 
			
		||||
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
                    'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
                    'X-API-TOKEN' => $this->token,
 | 
			
		||||
                ])->putJson('/api/v1/quotes/'.$arr['data']['id'], $arr['data']);
 | 
			
		||||
 | 
			
		||||
        $response->assertStatus(200);
 | 
			
		||||
 | 
			
		||||
        $arr = $response->json();
 | 
			
		||||
        
 | 
			
		||||
        $this->assertEquals(now()->addDay()->format('Y-m-d'), $arr['data']['due_date']);
 | 
			
		||||
        $this->assertEquals(now()->format('Y-m-d'), $arr['data']['partial_due_date']);
 | 
			
		||||
        $this->assertEquals(1, $arr['data']['partial']);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testQuoteToProjectConversion2()
 | 
			
		||||
 | 
			
		||||
@ -28,15 +28,17 @@ class NumberTest extends TestCase
 | 
			
		||||
            "22000.76" =>"22 000,76",
 | 
			
		||||
            "22000.76" =>"22.000,76",
 | 
			
		||||
            "22000.76" =>"22,000.76",
 | 
			
		||||
            "22000" =>"22 000",
 | 
			
		||||
            "22000" =>"22,000",
 | 
			
		||||
            "2201" => "2,201",
 | 
			
		||||
            "22001" =>"22 001",
 | 
			
		||||
            "22002" =>"22,002",
 | 
			
		||||
            "37123" => "37,123",
 | 
			
		||||
            "22" =>"22.000",
 | 
			
		||||
            "22000" =>"22.000,",
 | 
			
		||||
            "22000.76" =>"22000.76",
 | 
			
		||||
            "22000.76" =>"22000,76",
 | 
			
		||||
            "1022000.76" =>"1.022.000,76",
 | 
			
		||||
            "1022000.76" =>"1,022,000.76",
 | 
			
		||||
            // "1000000" =>"1,000,000",
 | 
			
		||||
            "1000000" =>"1,000,000",
 | 
			
		||||
            // "1000000" =>"1.000.000",
 | 
			
		||||
            "1022000.76" =>"1022000.76",
 | 
			
		||||
            "1022000.76" =>"1022000,76",
 | 
			
		||||
@ -48,7 +50,7 @@ class NumberTest extends TestCase
 | 
			
		||||
            "1" =>"1.00",
 | 
			
		||||
            "1" =>"1,00",
 | 
			
		||||
            "423545" =>"423545 €",
 | 
			
		||||
            // "423545" =>"423,545 €",
 | 
			
		||||
            "423545" =>"423,545 €",
 | 
			
		||||
            // "423545" =>"423.545 €",
 | 
			
		||||
            "1" =>"1,00 €",
 | 
			
		||||
            "1.02" =>"€ 1.02",
 | 
			
		||||
@ -58,7 +60,8 @@ class NumberTest extends TestCase
 | 
			
		||||
            "1000.02" =>"1.000,02 EURO",
 | 
			
		||||
            "9.975" => "9.975",
 | 
			
		||||
            "9975" => "9.975,",
 | 
			
		||||
            "9975" => "9.975,00"
 | 
			
		||||
            "9975" => "9.975,00",
 | 
			
		||||
            // "0.571" => "0,571",
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user