mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-10-31 14:47:31 -04: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 = '', | ||||
|     ) { | ||||
|     } | ||||
| } | ||||
| @ -26,48 +26,4 @@ class EDocSettings extends Data | ||||
|         return $this->FatturaPA ??= new FatturaPA(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 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,6 +327,65 @@ class PayPalRestPaymentDriver extends BaseDriver | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private function getPaymentSource(): array | ||||
|     { | ||||
| 
 | ||||
|         if($this->gateway_type_id == 1) { | ||||
| 
 | ||||
|             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", | ||||
|                     ], | ||||
|                 ], | ||||
|             ]; | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         return [ | ||||
|             "paypal" => [ | ||||
|                 "name" => [ | ||||
|                     "given_name" => $this->client->present()->first_name(), | ||||
|                     "surname" => $this->client->present()->last_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" | ||||
|                 ], | ||||
|             ], | ||||
|         ]; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private function createOrder(array $data): string | ||||
|     { | ||||
| 
 | ||||
| @ -338,27 +400,7 @@ class PayPalRestPaymentDriver extends BaseDriver | ||||
|         $order = [ | ||||
| 
 | ||||
|                 "intent" => "CAPTURE", | ||||
|                 "payment_source" => [ | ||||
|                     "paypal" => [ | ||||
| 
 | ||||
|                         "name" => [ | ||||
|                             "given_name" => $this->client->present()->first_name(), | ||||
|                             "surname" => $this->client->present()->last_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" | ||||
|                         ], | ||||
|                     ], | ||||
|                 ], | ||||
|                 "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