mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-10-31 11:17:34 -04:00 
			
		
		
		
	Merge remote-tracking branch 'origin/v5-develop' into v5-develop
This commit is contained in:
		
						commit
						c908819348
					
				
							
								
								
									
										41
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| --- | ||||
| name: Bug report | ||||
| about: Create a report to help us improve | ||||
| title: '' | ||||
| labels: triage | ||||
| assignees: '' | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| **What version of Invoice Ninja are you running? ie v4.5.25 / v5.0.30** | ||||
| 
 | ||||
| **What environment are you running?** | ||||
| Docker | ||||
| Shared Hosting | ||||
| ZIP | ||||
| Other | ||||
| 
 | ||||
| **Have you checked log files (storage/logs/) Please provide redacted output** | ||||
| 
 | ||||
| **Have you searched existing issues?** | ||||
| 
 | ||||
| **Have you reported this to Slack/forum before posting?** | ||||
| 
 | ||||
| **Describe the bug** | ||||
| A clear and concise description of what the bug is. | ||||
| 
 | ||||
| **Steps To Reproduce** | ||||
| Please list the steps to reproduce the issue | ||||
| 
 | ||||
| **Expected behavior** | ||||
| A clear and concise description of what you expected to happen. | ||||
| 
 | ||||
| **Screenshots** | ||||
| If applicable, add screenshots to help explain your problem. | ||||
| 
 | ||||
| **Additional context** | ||||
| Add any other context about the problem here. | ||||
| 
 | ||||
| <!-- Note: Before posting don't forget to check our "Troubleshooting" category in the [docs](https://invoiceninja.github.io/docs/self-host-troubleshooting/) (https://invoiceninja.github.io/docs/self-host-troubleshooting/) --> | ||||
| 
 | ||||
| **(v5) Can you replicate the issue on our demo site? https://demo.invoiceninja.com** | ||||
							
								
								
									
										24
									
								
								.github/ISSUE_TEMPLATE/feature_request.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								.github/ISSUE_TEMPLATE/feature_request.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| --- | ||||
| name: Feature request | ||||
| about: Suggest an idea for this project | ||||
| title: '' | ||||
| labels: feature request | ||||
| assignees: '' | ||||
| 
 | ||||
| --- | ||||
| 
 | ||||
| **What version of Invoice Ninja are you running? ie v4.5 / v5** | ||||
| 
 | ||||
| **What environment are you running?** | ||||
| Docker | ||||
| Shared Hosting | ||||
| ZIP | ||||
| Other | ||||
| 
 | ||||
| **Have you searched existing issues/requests?** | ||||
| 
 | ||||
| **Screenshots** | ||||
| If applicable, add screenshots to help explain your request/question. | ||||
| 
 | ||||
| **Additional context** | ||||
| Add any other context about the request/question here. | ||||
| @ -1 +1 @@ | ||||
| 5.5.48 | ||||
| 5.5.55 | ||||
| @ -119,6 +119,7 @@ class CheckData extends Command | ||||
|         $this->checkDuplicateRecurringInvoices(); | ||||
|         $this->checkOauthSanity(); | ||||
|         $this->checkVendorSettings(); | ||||
|         $this->checkClientSettings(); | ||||
|          | ||||
|         if(Ninja::isHosted()){ | ||||
|             $this->checkAccountStatuses(); | ||||
| @ -952,24 +953,24 @@ class CheckData extends Command | ||||
| 
 | ||||
|         if ($this->option('fix') == 'true') { | ||||
| 
 | ||||
|             Client::query()->whereNull('settings->currency_id')->cursor()->each(function ($client){ | ||||
|             // Client::query()->whereNull('settings->currency_id')->cursor()->each(function ($client){
 | ||||
| 
 | ||||
|                 if(is_array($client->settings) && count($client->settings) == 0) | ||||
|                 { | ||||
|                     $settings = ClientSettings::defaults(); | ||||
|                     $settings->currency_id = $client->company->settings->currency_id; | ||||
|                 } | ||||
|                 else { | ||||
|                     $settings = $client->settings; | ||||
|                     $settings->currency_id = $client->company->settings->currency_id; | ||||
|                 } | ||||
|             //     if(is_array($client->settings) && count($client->settings) == 0)
 | ||||
|             //     {
 | ||||
|             //         $settings = ClientSettings::defaults();
 | ||||
|             //         $settings->currency_id = $client->company->settings->currency_id;
 | ||||
|             //     }
 | ||||
|             //     else {
 | ||||
|             //         $settings = $client->settings;
 | ||||
|             //         $settings->currency_id = $client->company->settings->currency_id;
 | ||||
|             //     }
 | ||||
| 
 | ||||
|                 $client->settings = $settings; | ||||
|                 $client->save(); | ||||
|             //     $client->settings = $settings;
 | ||||
|             //     $client->save();
 | ||||
| 
 | ||||
|                 $this->logMessage("Fixing currency for # {$client->id}"); | ||||
|             //     $this->logMessage("Fixing currency for # {$client->id}");
 | ||||
| 
 | ||||
|             }); | ||||
|             // });
 | ||||
| 
 | ||||
| 
 | ||||
|             Client::query()->whereNull('country_id')->cursor()->each(function ($client){ | ||||
|  | ||||
| @ -306,8 +306,8 @@ class CreateSingleAccount extends Command | ||||
| 
 | ||||
|         $webhook_config = [ | ||||
|             'post_purchase_url' => 'http://ninja.test:8000/api/admin/plan', | ||||
|             'post_purchase_rest_method' => 'POST', | ||||
|             'post_purchase_headers' => [], | ||||
|             'post_purchase_rest_method' => 'post', | ||||
|             'post_purchase_headers' => [config('ninja.ninja_hosted_header') => config('ninja.ninja_hosted_secret')], | ||||
|         ]; | ||||
| 
 | ||||
|         $sub = SubscriptionFactory::create($company->id, $user->id); | ||||
|  | ||||
| @ -437,7 +437,7 @@ class CreateTestData extends Command | ||||
|             'company_id' => $client->company->id, | ||||
|         ]); | ||||
| 
 | ||||
|         Document::factory()->count(5)->create([ | ||||
|         Document::factory()->count(1)->create([ | ||||
|             'user_id' => $client->user->id, | ||||
|             'company_id' => $client->company_id, | ||||
|             'documentable_type' => Vendor::class, | ||||
|  | ||||
| @ -56,7 +56,7 @@ class ReactBuilder extends Command | ||||
|         $directoryIterator = new \RecursiveDirectoryIterator(public_path('react'), \RecursiveDirectoryIterator::SKIP_DOTS); | ||||
| 
 | ||||
|         foreach (new \RecursiveIteratorIterator($directoryIterator) as $file) { | ||||
|             if (str_contains($file->getFileName(), '.js')) { | ||||
|             if (str_contains($file->getFileName(), '.js') && !strpos($file->getFileName(), '.json')) { | ||||
|                 if (str_contains($file->getFileName(), 'index.')) { | ||||
|                     $includes .= '<script type="module" crossorigin src="/react/'.$file->getFileName().'"></script>'."\n"; | ||||
|                 } else { | ||||
|  | ||||
| @ -27,7 +27,7 @@ class TranslationsExport extends Command | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'ninja:translations'; | ||||
|     protected $signature = 'ninja:translations {--type=} {--path=}'; | ||||
| 
 | ||||
|     /** | ||||
|      * The console command description. | ||||
| @ -36,8 +36,11 @@ class TranslationsExport extends Command | ||||
|      */ | ||||
|     protected $description = 'Transform translations to json'; | ||||
| 
 | ||||
|     protected $log = ''; | ||||
| 
 | ||||
|     private array $langs = [ | ||||
|         'ar', | ||||
|         'bg', | ||||
|         'ca', | ||||
|         'cs', | ||||
|         'da', | ||||
| @ -47,10 +50,12 @@ class TranslationsExport extends Command | ||||
|         'en_GB', | ||||
|         'es', | ||||
|         'es_ES', | ||||
|         'et', | ||||
|         'fa', | ||||
|         'fi', | ||||
|         'fr', | ||||
|         'fr_CA', | ||||
|         'he', | ||||
|         'hr', | ||||
|         'it', | ||||
|         'ja', | ||||
| @ -65,7 +70,9 @@ class TranslationsExport extends Command | ||||
|         'ro', | ||||
|         'ru_RU', | ||||
|         'sl', | ||||
|         'sk', | ||||
|         'sq', | ||||
|         'sr', | ||||
|         'sv', | ||||
|         'th', | ||||
|         'tr_TR', | ||||
| @ -88,6 +95,49 @@ class TranslationsExport extends Command | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         $type =$this->option('type') ?? 'export'; | ||||
| 
 | ||||
|         if($type == 'import') | ||||
|             $this->import(); | ||||
| 
 | ||||
|         if($type == 'export') | ||||
|             $this->export(); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private function import() | ||||
|     { | ||||
|         //loop and
 | ||||
| 
 | ||||
|         foreach($this->langs as $lang) | ||||
|         { | ||||
| 
 | ||||
|             $import_file = "textsphp_{$lang}.php"; | ||||
|             $dir = $this->option('path') ?? storage_path('lang_import/'); | ||||
|             $path = $dir.$import_file; | ||||
| 
 | ||||
|             if(file_exists($path)){ | ||||
|                 $this->logMessage($path); | ||||
| 
 | ||||
|                 $trans = file_get_contents($path); | ||||
| 
 | ||||
|                 file_put_contents(lang_path("/{$lang}/texts.php"), $trans); | ||||
| 
 | ||||
|             } | ||||
|             else{ | ||||
| 
 | ||||
|                 $this->logMessage("Could not open file"); | ||||
|                 $this->logMessage($path); | ||||
|              | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private function export() | ||||
|     { | ||||
|         Storage::disk('local')->makeDirectory('lang'); | ||||
| 
 | ||||
| @ -99,4 +149,12 @@ class TranslationsExport extends Command | ||||
|             Storage::disk('local')->put("lang/{$lang}/{$lang}.json", json_encode(Arr::dot($translations), JSON_UNESCAPED_UNICODE)); | ||||
|         }  | ||||
|     } | ||||
| 
 | ||||
|     private function logMessage($str) | ||||
|     { | ||||
|         $str = date('Y-m-d h:i:s').' '.$str; | ||||
|         $this->info($str); | ||||
|         $this->log .= $str."\n"; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -22,7 +22,9 @@ use App\Jobs\Ninja\CompanySizeCheck; | ||||
| use App\Jobs\Ninja\QueueSize; | ||||
| use App\Jobs\Ninja\SystemMaintenance; | ||||
| use App\Jobs\Ninja\TaskScheduler; | ||||
| use App\Jobs\Invoice\InvoiceCheckLateWebhook; | ||||
| use App\Jobs\Quote\QuoteCheckExpired; | ||||
| use App\Jobs\Subscription\CleanStaleInvoiceOrder; | ||||
| use App\Jobs\Util\DiskCleanup; | ||||
| use App\Jobs\Util\ReminderJob; | ||||
| use App\Jobs\Util\SchedulerCheck; | ||||
| @ -68,12 +70,18 @@ class Kernel extends ConsoleKernel | ||||
|         /* Sends recurring invoices*/ | ||||
|         $schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping()->name('recurring-invoice-job')->onOneServer(); | ||||
| 
 | ||||
|         /* Stale Invoice Cleanup*/ | ||||
|         $schedule->job(new CleanStaleInvoiceOrder)->hourlyAt(30)->withoutOverlapping()->name('stale-invoice-job')->onOneServer(); | ||||
| 
 | ||||
|         /* Sends recurring invoices*/ | ||||
|         $schedule->job(new RecurringExpensesCron)->dailyAt('00:10')->withoutOverlapping()->name('recurring-expense-job')->onOneServer(); | ||||
| 
 | ||||
|         /* Fires notifications for expired Quotes */ | ||||
|         $schedule->job(new QuoteCheckExpired)->dailyAt('05:10')->withoutOverlapping()->name('quote-expired-job')->onOneServer(); | ||||
| 
 | ||||
|         /* Fires webhooks for overdue Invoice */ | ||||
|         $schedule->job(new InvoiceCheckLateWebhook)->dailyAt('07:00')->withoutOverlapping()->name('invoice-overdue-job')->onOneServer(); | ||||
| 
 | ||||
|         /* Performs auto billing */ | ||||
|         $schedule->job(new AutoBillCron)->dailyAt('06:20')->withoutOverlapping()->name('auto-bill-job')->onOneServer(); | ||||
| 
 | ||||
| @ -81,7 +89,7 @@ class Kernel extends ConsoleKernel | ||||
|         $schedule->job(new SchedulerCheck)->dailyAt('01:10')->withoutOverlapping(); | ||||
| 
 | ||||
|         /* Checks for scheduled tasks */ | ||||
|         $schedule->job(new TaskScheduler())->dailyAt('06:50')->withoutOverlapping()->name('task-scheduler-job')->onOneServer(); | ||||
|         $schedule->job(new TaskScheduler())->hourlyAt(10)->withoutOverlapping()->name('task-scheduler-job')->onOneServer(); | ||||
| 
 | ||||
|         /* Performs system maintenance such as pruning the backup table */ | ||||
|         $schedule->job(new SystemMaintenance)->sundays()->at('02:30')->withoutOverlapping()->name('system-maintenance-job')->onOneServer(); | ||||
|  | ||||
| @ -93,6 +93,10 @@ class ClientRegistrationFields | ||||
|                 'key' => 'vat_number', | ||||
|                 'required' => false, | ||||
|             ], | ||||
|             [ | ||||
|                 'key' => 'currency_id', | ||||
|                 'required' => false, | ||||
|             ], | ||||
|         ]; | ||||
| 
 | ||||
|         return $data; | ||||
|  | ||||
| @ -441,7 +441,22 @@ class CompanySettings extends BaseSettings | ||||
| 
 | ||||
|     public $send_email_on_mark_paid = false; | ||||
| 
 | ||||
|     public $postmark_secret = ''; | ||||
|      | ||||
|     public $mailgun_secret = ''; | ||||
|      | ||||
|     public $mailgun_domain = ''; | ||||
| 
 | ||||
|     public $auto_bill_standard_invoices = false; | ||||
| 
 | ||||
|     public $email_alignment = 'center'; // center , left, right
 | ||||
| 
 | ||||
|     public static $casts = [ | ||||
|         'email_alignment'                    => 'string', | ||||
|         'auto_bill_standard_invoices'        => 'bool', | ||||
|         'postmark_secret'                    => 'string', | ||||
|         'mailgun_secret'                     => 'string', | ||||
|         'mailgun_domain'                     => 'string', | ||||
|         'send_email_on_mark_paid'            => 'bool', | ||||
|         'vendor_portal_enable_uploads'       => 'bool', | ||||
|         'besr_id'                            => 'string', | ||||
|  | ||||
| @ -235,12 +235,17 @@ class EmailTemplateDefaults | ||||
| 
 | ||||
|     public static function emailStatementSubject() | ||||
|     { | ||||
|         return ''; | ||||
|         return ctrans('texts.your_statement'); | ||||
|     } | ||||
| 
 | ||||
|     public static function emailStatementTemplate() | ||||
|     { | ||||
|         return ''; | ||||
| 
 | ||||
|         $statement_message = '<p>$client<br><br>'.self::transformText('client_statement_body').'<br></p>'; | ||||
| 
 | ||||
|         return $statement_message; | ||||
| 
 | ||||
|         // return ctrans('texts.client_statement_body', ['start_date' => '$start_date', 'end_date' => '$end_date']);
 | ||||
|     } | ||||
| 
 | ||||
|     private static function transformText($string) | ||||
|  | ||||
							
								
								
									
										96
									
								
								app/DataMapper/Schedule/ClientStatement.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								app/DataMapper/Schedule/ClientStatement.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,96 @@ | ||||
| <?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\DataMapper; | ||||
| 
 | ||||
| use App\Models\Client; | ||||
| use stdClass; | ||||
| 
 | ||||
| class ClientStatement | ||||
| { | ||||
| 
 | ||||
|     /** | ||||
|      * Defines the template name | ||||
|      *  | ||||
|      * @var string | ||||
|      */ | ||||
|     public string $template = 'client_statement'; | ||||
| 
 | ||||
|     /** | ||||
|      * An array of clients hashed_ids | ||||
|      * | ||||
|      * Leave blank if this action should apply to all clients | ||||
|      *  | ||||
|      * @var array | ||||
|      */ | ||||
|     public array $clients = []; | ||||
| 
 | ||||
|     /** | ||||
|      * The consts to be used to define the date_range variable of the statement | ||||
|      */ | ||||
|     public const THIS_MONTH = 'this_month'; | ||||
|     public const THIS_QUARTER = 'this_quarter'; | ||||
|     public const THIS_YEAR = 'this_year'; | ||||
|     public const PREVIOUS_MONTH = 'previous_month'; | ||||
|     public const PREVIOUS_QUARTER = 'previous_quarter'; | ||||
|     public const PREVIOUS_YEAR = 'previous_year'; | ||||
|     public const CUSTOM_RANGE = "custom_range"; | ||||
| 
 | ||||
|     /** | ||||
|      * The date range the statement should include | ||||
|      *  | ||||
|      * @var string | ||||
|      */ | ||||
|     public string $date_range = 'this_month'; | ||||
| 
 | ||||
|     /** | ||||
|      * If a custom range is select for the date range then | ||||
|      * the start_date should be supplied in Y-m-d format | ||||
|      *  | ||||
|      * @var string | ||||
|      */ | ||||
|     public string $start_date = ''; | ||||
| 
 | ||||
|     /** | ||||
|      * If a custom range is select for the date range then | ||||
|      * the end_date should be supplied in Y-m-d format | ||||
|      *  | ||||
|      * @var string | ||||
|      */ | ||||
|     public string $end_date = ''; | ||||
| 
 | ||||
|     /** | ||||
|      * Flag which allows the payment table | ||||
|      * to be shown | ||||
|      * | ||||
|      * @var boolean | ||||
|      */ | ||||
|     public bool $show_payments_table = true; | ||||
| 
 | ||||
|     /** | ||||
|      * Flag which allows the aging table | ||||
|      * to be shown | ||||
|      * | ||||
|      * @var boolean | ||||
|      */ | ||||
|     public bool $show_aging_table = true; | ||||
| 
 | ||||
|     /** | ||||
|      * String const which defines whether | ||||
|      * the invoices to be shown are either | ||||
|      * paid or unpaid | ||||
|      *  | ||||
|      * @var string | ||||
|      */ | ||||
|     public string $status = 'paid'; // paid | unpaid
 | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										138
									
								
								app/Export/CSV/ProductSalesExport.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								app/Export/CSV/ProductSalesExport.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,138 @@ | ||||
| <?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\Export\CSV; | ||||
| 
 | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Models\Client; | ||||
| use App\Models\Company; | ||||
| use App\Models\Credit; | ||||
| use App\Models\Document; | ||||
| use App\Models\Invoice; | ||||
| use App\Models\Product; | ||||
| use App\Transformers\ProductTransformer; | ||||
| use App\Utils\Ninja; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use League\Csv\Writer; | ||||
| use Illuminate\Support\Carbon; | ||||
| 
 | ||||
| class ProductSalesExport extends BaseExport | ||||
| { | ||||
|     private Company $company; | ||||
| 
 | ||||
|     protected array $input; | ||||
| 
 | ||||
|     protected $date_key = 'created_at'; | ||||
| 
 | ||||
|     protected array $entity_keys = [ | ||||
|         'custom_value1' => 'custom_value1', | ||||
|         'custom_value2' => 'custom_value2', | ||||
|         'custom_value3' => 'custom_value3', | ||||
|         'custom_value4' => 'custom_value4', | ||||
|         'product_key' => 'product_key', | ||||
|         'notes' => 'notes', | ||||
|         'cost' => 'cost', | ||||
|         'price' => 'price', | ||||
|         'quantity' => 'quantity', | ||||
|         'tax_rate1' => 'tax_rate1', | ||||
|         'tax_rate2' => 'tax_rate2', | ||||
|         'tax_rate3' => 'tax_rate3', | ||||
|         'tax_name1' => 'tax_name1', | ||||
|         'tax_name2' => 'tax_name2', | ||||
|         'tax_name3' => 'tax_name3', | ||||
|         'is_amount_discount' => 'is_amount_discount', | ||||
|         'discount' => 'discount', | ||||
|         'line_total' => 'line_total', | ||||
|         'gross_line_total' => 'gross_line_total', | ||||
|         'status' => 'status', | ||||
|         'date' => 'date', | ||||
|         'currency' => 'currency', | ||||
|         'client' => 'client', | ||||
|     ]; | ||||
| 
 | ||||
|     private array $decorate_keys = [ | ||||
|         'client', | ||||
|         'currency', | ||||
|         'date', | ||||
|     ]; | ||||
| 
 | ||||
|     public function __construct(Company $company, array $input) | ||||
|     { | ||||
|         $this->company = $company; | ||||
|         $this->input = $input; | ||||
|     } | ||||
| 
 | ||||
|     public function run() | ||||
|     { | ||||
|         MultiDB::setDb($this->company->db); | ||||
|         App::forgetInstance('translator'); | ||||
|         App::setLocale($this->company->locale()); | ||||
|         $t = app('translator'); | ||||
|         $t->replace(Ninja::transformTranslations($this->company->settings)); | ||||
| 
 | ||||
|         //load the CSV document from a string
 | ||||
|         $this->csv = Writer::createFromString(); | ||||
| 
 | ||||
|         if (count($this->input['report_keys']) == 0) { | ||||
|             $this->input['report_keys'] = array_values($this->entity_keys); | ||||
|         } | ||||
| 
 | ||||
|         //insert the header
 | ||||
|         $this->csv->insertOne($this->buildHeader()); | ||||
| 
 | ||||
|         $query = Invoice::query() | ||||
|                         ->withTrashed() | ||||
|                         ->where('company_id', $this->company->id) | ||||
|                         ->where('is_deleted', 0) | ||||
|                         ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL, Invoice::STATUS_PAID]); | ||||
| 
 | ||||
|         $query = $this->addDateRange($query); | ||||
| 
 | ||||
|         $query->cursor() | ||||
|               ->each(function ($invoice) { | ||||
| 
 | ||||
|                   foreach($invoice->line_items as $item) | ||||
|                     $this->csv->insertOne($this->buildRow($invoice, $item)); | ||||
| 
 | ||||
|               }); | ||||
| 
 | ||||
|         return $this->csv->toString(); | ||||
|     } | ||||
| 
 | ||||
|     private function buildRow($invoice, $invoice_item) :array | ||||
|     { | ||||
|         $transformed_entity = (array)$invoice_item; | ||||
| 
 | ||||
|         $entity = []; | ||||
| 
 | ||||
|         foreach (array_values($this->input['report_keys']) as $key) { | ||||
|             $keyval = array_search($key, $this->entity_keys); | ||||
| 
 | ||||
|             if (array_key_exists($key, $transformed_entity)) { | ||||
|                 $entity[$keyval] = $transformed_entity[$key]; | ||||
|             } else { | ||||
|                 $entity[$keyval] = ''; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $this->decorateAdvancedFields($invoice, $entity); | ||||
|     } | ||||
| 
 | ||||
|     private function decorateAdvancedFields(Invoice $invoice, $entity) :array | ||||
|     { | ||||
|         $entity['client'] = $invoice->client->present()->name(); | ||||
|         $entity['currency'] = $invoice->client->currency()->code; | ||||
|         $entity['status'] = $invoice->stringStatus($invoice->status_id); | ||||
|         $entity['date'] = Carbon::parse($invoice->date)->format($this->company->date_format()); | ||||
| 
 | ||||
|         return $entity; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										32
									
								
								app/Factory/SchedulerFactory.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								app/Factory/SchedulerFactory.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| <?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\Factory; | ||||
| 
 | ||||
| use App\Models\Scheduler; | ||||
| 
 | ||||
| class SchedulerFactory | ||||
| { | ||||
|     public static function create($company_id, $user_id) :Scheduler | ||||
|     { | ||||
|         $scheduler = new Scheduler; | ||||
| 
 | ||||
|         $scheduler->name = ''; | ||||
|         $scheduler->company_id = $company_id; | ||||
|         $scheduler->user_id = $user_id; | ||||
|         $scheduler->parameters = []; | ||||
|         $scheduler->is_paused = false; | ||||
|         $scheduler->is_deleted = false; | ||||
|         $scheduler->template = ''; | ||||
|          | ||||
|         return $scheduler; | ||||
|     } | ||||
| } | ||||
| @ -77,28 +77,45 @@ class BankTransactionFilters extends QueryFilters | ||||
| 
 | ||||
|         $status_parameters = explode(',', $value); | ||||
| 
 | ||||
|         $status_array = []; | ||||
|          | ||||
|         $debit_or_withdrawal_array = []; | ||||
| 
 | ||||
|         if (in_array('all', $status_parameters)) { | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('unmatched', $status_parameters)) { | ||||
|             $this->builder->where('status_id', BankTransaction::STATUS_UNMATCHED); | ||||
|             $status_array[] = BankTransaction::STATUS_UNMATCHED; | ||||
|             // $this->builder->orWhere('status_id', BankTransaction::STATUS_UNMATCHED);
 | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('matched', $status_parameters)) { | ||||
|             $this->builder->where('status_id', BankTransaction::STATUS_MATCHED); | ||||
|             $status_array[] = BankTransaction::STATUS_MATCHED; | ||||
|             // $this->builder->where('status_id', BankTransaction::STATUS_MATCHED);
 | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('converted', $status_parameters)) { | ||||
|             $this->builder->where('status_id', BankTransaction::STATUS_CONVERTED); | ||||
|             $status_array[] = BankTransaction::STATUS_CONVERTED; | ||||
|             // $this->builder->where('status_id', BankTransaction::STATUS_CONVERTED);
 | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('deposits', $status_parameters)) { | ||||
|             $this->builder->where('base_type', 'CREDIT'); | ||||
|             $debit_or_withdrawal_array[] = 'CREDIT'; | ||||
|             // $this->builder->where('base_type', 'CREDIT');
 | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('withdrawals', $status_parameters)) { | ||||
|             $this->builder->where('base_type', 'DEBIT'); | ||||
|             $debit_or_withdrawal_array[] = 'DEBIT'; | ||||
|             // $this->builder->where('base_type', 'DEBIT');
 | ||||
|         } | ||||
| 
 | ||||
|         if(count($status_array) >=1) { | ||||
|             $this->builder->whereIn('status_id', $status_array); | ||||
|         } | ||||
| 
 | ||||
|         if(count($debit_or_withdrawal_array) >=1) { | ||||
|             $this->builder->orWhereIn('base_type', $debit_or_withdrawal_array); | ||||
|         } | ||||
| 
 | ||||
|         return $this->builder; | ||||
|  | ||||
| @ -193,9 +193,7 @@ class ClientFilters extends QueryFilters | ||||
|             ->where('clients.company_id', '=', $company_id) | ||||
|             ->where('client_contacts.is_primary', '=', true) | ||||
|             ->where('client_contacts.deleted_at', '=', null) | ||||
|             //->whereRaw('(clients.name != "" or contacts.first_name != "" or contacts.last_name != "" or contacts.email != "")') // filter out buy now invoices
 | ||||
|             ->select( | ||||
|                // DB::raw('COALESCE(clients.currency_id, companies.currency_id) currency_id'),
 | ||||
|                 DB::raw('COALESCE(clients.country_id, companies.country_id) country_id'), | ||||
|                 DB::raw("CONCAT(COALESCE(client_contacts.first_name, ''), ' ', COALESCE(client_contacts.last_name, '')) contact"), | ||||
|                 'clients.id', | ||||
| @ -238,7 +236,6 @@ class ClientFilters extends QueryFilters | ||||
|      */ | ||||
|     public function entityFilter() | ||||
|     { | ||||
|         //return $this->builder->whereCompanyId(auth()->user()->company()->id);
 | ||||
|         return $this->builder->company(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -105,7 +105,6 @@ class DesignFilters extends QueryFilters | ||||
|         $query = DB::table('designs') | ||||
|             ->join('companies', 'companies.id', '=', 'designs.company_id') | ||||
|             ->where('designs.company_id', '=', $company_id) | ||||
|             //->whereRaw('(designs.name != "" or contacts.first_name != "" or contacts.last_name != "" or contacts.email != "")') // filter out buy now invoices
 | ||||
|             ->select( | ||||
|                 'designs.id', | ||||
|                 'designs.name', | ||||
|  | ||||
							
								
								
									
										124
									
								
								app/Filters/ExpenseCategoryFilters.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								app/Filters/ExpenseCategoryFilters.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,124 @@ | ||||
| <?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\Filters; | ||||
| 
 | ||||
| use App\Models\Expense; | ||||
| use App\Models\User; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Support\Facades\DB; | ||||
| use Illuminate\Support\Facades\Gate; | ||||
| 
 | ||||
| /** | ||||
|  * ExpenseCategoryFilters. | ||||
|  */ | ||||
| class ExpenseCategoryFilters extends QueryFilters | ||||
| { | ||||
|     /** | ||||
|      * Filter based on search text. | ||||
|      * | ||||
|      * @param string query filter | ||||
|      * @return Builder | ||||
|      * @deprecated | ||||
|      */ | ||||
|     public function filter(string $filter = '') : Builder | ||||
|     { | ||||
|         if (strlen($filter) == 0) { | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         return  $this->builder->where('expense_categories.name', 'like', '%'.$filter.'%'); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Filters the list based on the status | ||||
|      * archived, active, deleted. | ||||
|      * | ||||
|      * @param string filter | ||||
|      * @return Builder | ||||
|      */ | ||||
|     public function status(string $filter = '') : Builder | ||||
|     { | ||||
|         if (strlen($filter) == 0) { | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         $table = 'expense_categories'; | ||||
|         $filters = explode(',', $filter); | ||||
| 
 | ||||
|         return $this->builder->where(function ($query) use ($filters, $table) { | ||||
|             $query->whereNull($table.'.id'); | ||||
| 
 | ||||
|             if (in_array(parent::STATUS_ACTIVE, $filters)) { | ||||
|                 $query->orWhereNull($table.'.deleted_at'); | ||||
|             } | ||||
| 
 | ||||
|             if (in_array(parent::STATUS_ARCHIVED, $filters)) { | ||||
|                 $query->orWhere(function ($query) use ($table) { | ||||
|                     $query->whereNotNull($table.'.deleted_at'); | ||||
| 
 | ||||
|                     if (! in_array($table, ['users'])) { | ||||
|                         $query->where($table.'.is_deleted', '=', 0); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|             if (in_array(parent::STATUS_DELETED, $filters)) { | ||||
|                 $query->orWhere($table.'.is_deleted', '=', 1); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sorts the list based on $sort. | ||||
|      * | ||||
|      * @param string sort formatted as column|asc | ||||
|      * @return Builder | ||||
|      */ | ||||
|     public function sort(string $sort) : Builder | ||||
|     { | ||||
|         $sort_col = explode('|', $sort); | ||||
| 
 | ||||
|         if (is_array($sort_col) && in_array($sort_col[1], ['asc', 'desc']) && in_array($sort_col[0], ['name'])) { | ||||
|             return $this->builder->orderBy($sort_col[0], $sort_col[1]); | ||||
|         } | ||||
| 
 | ||||
|         return $this->builder; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the base query. | ||||
|      * | ||||
|      * @param int company_id | ||||
|      * @param User $user | ||||
|      * @return Builder | ||||
|      * @deprecated | ||||
|      */ | ||||
|     public function baseQuery(int $company_id, User $user) : Builder | ||||
|     { | ||||
| 
 | ||||
|         return $this->builder; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Filters the query by the users company ID. | ||||
|      * | ||||
|      * @return Illuminate\Database\Query\Builder | ||||
|      */ | ||||
|     public function entityFilter() | ||||
|     { | ||||
| 
 | ||||
|         //return $this->builder->whereCompanyId(auth()->user()->company()->id);
 | ||||
|         return $this->builder->company(); | ||||
|     } | ||||
| } | ||||
| @ -69,25 +69,54 @@ class ExpenseFilters extends QueryFilters | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('logged', $status_parameters)) { | ||||
|             $this->builder->where('amount', '>', 0); | ||||
|         } | ||||
|         $this->builder->whereNested(function ($query) use($status_parameters){ | ||||
| 
 | ||||
|         if (in_array('pending', $status_parameters)) { | ||||
|             $this->builder->whereNull('invoice_id')->whereNotNull('payment_date'); | ||||
|         } | ||||
|             if (in_array('logged', $status_parameters)) { | ||||
| 
 | ||||
|         if (in_array('invoiced', $status_parameters)) { | ||||
|             $this->builder->whereNotNull('invoice_id'); | ||||
|         } | ||||
|                 $query->orWhere(function ($query){ | ||||
|                     $query->where('amount', '>', 0) | ||||
|                           ->whereNull('invoice_id') | ||||
|                           ->whereNull('payment_date'); | ||||
|                 }); | ||||
|                  | ||||
|         if (in_array('paid', $status_parameters)) { | ||||
|             $this->builder->whereNotNull('payment_date'); | ||||
|         } | ||||
|             } | ||||
| 
 | ||||
|         if (in_array('unpaid', $status_parameters)) { | ||||
|             $this->builder->whereNull('payment_date'); | ||||
|         } | ||||
|             if (in_array('pending', $status_parameters)) { | ||||
| 
 | ||||
|                 $query->orWhere(function ($query){ | ||||
|                     $query->where('should_be_invoiced',true) | ||||
|                           ->whereNull('invoice_id'); | ||||
|                 }); | ||||
|                  | ||||
|             } | ||||
| 
 | ||||
|             if (in_array('invoiced', $status_parameters)) { | ||||
| 
 | ||||
|                 $query->orWhere(function ($query){ | ||||
|                     $query->whereNotNull('invoice_id'); | ||||
|                 }); | ||||
|                  | ||||
|             } | ||||
| 
 | ||||
|             if (in_array('paid', $status_parameters)) { | ||||
| 
 | ||||
|                 $query->orWhere(function ($query){ | ||||
|                     $query->whereNotNull('payment_date'); | ||||
|                 }); | ||||
|                  | ||||
|             } | ||||
| 
 | ||||
|             if (in_array('unpaid', $status_parameters)) { | ||||
| 
 | ||||
|                 $query->orWhere(function ($query){ | ||||
|                     $query->whereNull('payment_date'); | ||||
|                 }); | ||||
|                  | ||||
|             } | ||||
| 
 | ||||
|         }); | ||||
| 
 | ||||
|         // nlog($this->builder->toSql());
 | ||||
| 
 | ||||
|         return $this->builder; | ||||
|     } | ||||
| @ -176,9 +205,7 @@ class ExpenseFilters extends QueryFilters | ||||
|         $query = DB::table('expenses') | ||||
|             ->join('companies', 'companies.id', '=', 'expenses.company_id') | ||||
|             ->where('expenses.company_id', '=', $company_id) | ||||
|             //->whereRaw('(expenses.name != "" or contacts.first_name != "" or contacts.last_name != "" or contacts.email != "")') // filter out buy now invoices
 | ||||
|             ->select( | ||||
|                // DB::raw('COALESCE(expenses.currency_id, companies.currency_id) currency_id'),
 | ||||
|                 DB::raw('COALESCE(expenses.country_id, companies.country_id) country_id'), | ||||
|                 DB::raw("CONCAT(COALESCE(expense_contacts.first_name, ''), ' ', COALESCE(expense_contacts.last_name, '')) contact"), | ||||
|                 'expenses.id', | ||||
| @ -212,8 +239,6 @@ class ExpenseFilters extends QueryFilters | ||||
|      */ | ||||
|     public function entityFilter() | ||||
|     { | ||||
| 
 | ||||
|         //return $this->builder->whereCompanyId(auth()->user()->company()->id);
 | ||||
|         return $this->builder->company(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -16,6 +16,8 @@ use App\Models\User; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Support\Carbon; | ||||
| use InvalidArgumentException; | ||||
| use RuntimeException; | ||||
| 
 | ||||
| /** | ||||
|  * InvoiceFilters. | ||||
| @ -45,20 +47,27 @@ class InvoiceFilters extends QueryFilters | ||||
| 
 | ||||
|         $status_parameters = explode(',', $value); | ||||
| 
 | ||||
|         $invoice_filters = []; | ||||
| 
 | ||||
|         if (in_array('all', $status_parameters)) { | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('paid', $status_parameters)) { | ||||
|             $this->builder->where('status_id', Invoice::STATUS_PAID); | ||||
|             $invoice_filters[] = Invoice::STATUS_PAID; | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('unpaid', $status_parameters)) { | ||||
|             $this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]); | ||||
|             $invoice_filters[] = Invoice::STATUS_SENT; | ||||
|             $invoice_filters[] = Invoice::STATUS_PARTIAL; | ||||
|         } | ||||
| 
 | ||||
|         if(count($invoice_filters) >0){ | ||||
|             $this->builder->whereIn('status_id', $invoice_filters); | ||||
|         } | ||||
|          | ||||
|         if (in_array('overdue', $status_parameters)) { | ||||
|             $this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) | ||||
|             $this->builder->orWhereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) | ||||
|                             ->where('due_date', '<', Carbon::now()) | ||||
|                             ->orWhere('partial_due_date', '<', Carbon::now()); | ||||
|         } | ||||
| @ -136,6 +145,10 @@ class InvoiceFilters extends QueryFilters | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return Builder  | ||||
|      * @throws RuntimeException  | ||||
|      */ | ||||
|     public function without_deleted_clients() | ||||
|     { | ||||
| 
 | ||||
| @ -144,6 +157,10 @@ class InvoiceFilters extends QueryFilters | ||||
|                        }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return Builder  | ||||
|      * @throws InvalidArgumentException  | ||||
|      */ | ||||
|     public function upcoming() | ||||
|     { | ||||
|         return $this->builder | ||||
| @ -154,6 +171,10 @@ class InvoiceFilters extends QueryFilters | ||||
|                     ->orderBy('due_date', 'ASC'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @return void  | ||||
|      * @throws InvalidArgumentException  | ||||
|      */ | ||||
|     public function overdue() | ||||
|     { | ||||
|         $this->builder->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) | ||||
| @ -165,6 +186,11 @@ class InvoiceFilters extends QueryFilters | ||||
|                 ->orderBy('due_date', 'ASC'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param string $client_id  | ||||
|      * @return Builder  | ||||
|      * @throws InvalidArgumentException  | ||||
|      */ | ||||
|     public function payable(string $client_id = '') | ||||
|     { | ||||
|         if (strlen($client_id) == 0) { | ||||
| @ -222,8 +248,20 @@ class InvoiceFilters extends QueryFilters | ||||
|         } else {             | ||||
|             return $this->builder->company()->with(['invitations.company'], ['documents.company']); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| //            return $this->builder->whereCompanyId(auth()->user()->company()->id);
 | ||||
|     /** | ||||
|      * @param string $filter  | ||||
|      * @return Builder  | ||||
|      * @throws InvalidArgumentException  | ||||
|      */ | ||||
|     public function private_notes($filter = '') :Builder | ||||
|     { | ||||
|         if (strlen($filter) == 0) { | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         return $this->builder->where('private_notes', 'LIKE', '%'.$filter.'%'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -84,12 +84,17 @@ class PaymentFilters extends QueryFilters | ||||
|     /** | ||||
|      * Returns a list of payments that can be matched to bank transactions | ||||
|      */ | ||||
|     public function match_transactions($value = '') | ||||
|     public function match_transactions($value = 'true') :Builder | ||||
|     { | ||||
| 
 | ||||
|         if($value == 'true') | ||||
|         { | ||||
|             return $this->builder->where('is_deleted',0)->whereNull('transaction_id'); | ||||
|         if($value == 'true'){ | ||||
|             return $this->builder | ||||
|                         ->where('is_deleted',0) | ||||
|                         ->where(function ($query){ | ||||
|                             $query->whereNull('transaction_id') | ||||
|                             ->orWhere("transaction_id",""); | ||||
|                         }); | ||||
|                          | ||||
|         } | ||||
| 
 | ||||
|         return $this->builder; | ||||
|  | ||||
| @ -42,20 +42,26 @@ class PurchaseOrderFilters extends QueryFilters | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         $po_status = []; | ||||
| 
 | ||||
|         if (in_array('draft', $status_parameters)) { | ||||
|             $this->builder->where('status_id', PurchaseOrder::STATUS_DRAFT); | ||||
|             $po_status[] = PurchaseOrder::STATUS_DRAFT; | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('sent', $status_parameters)) { | ||||
|             $this->builder->where('status_id', PurchaseOrder::STATUS_SENT); | ||||
|             $po_status[] = PurchaseOrder::STATUS_SENT; | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('accepted', $status_parameters)) { | ||||
|             $this->builder->where('status_id', PurchaseOrder::STATUS_ACCEPTED); | ||||
|             $po_status[] = PurchaseOrder::STATUS_ACCEPTED; | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('cancelled', $status_parameters)) { | ||||
|             $this->builder->where('status_id', PurchaseOrder::STATUS_CANCELLED); | ||||
|             $po_status[] = PurchaseOrder::STATUS_CANCELLED; | ||||
|         } | ||||
| 
 | ||||
|         if(count($status_parameters) >=1) { | ||||
|             $this->builder->whereIn('status_id', $status_parameters); | ||||
|         } | ||||
| 
 | ||||
|         return $this->builder; | ||||
|  | ||||
| @ -15,6 +15,7 @@ namespace App\Filters; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Carbon; | ||||
| 
 | ||||
| /** | ||||
|  * Class QueryFilters. | ||||
| @ -173,22 +174,30 @@ abstract class QueryFilters | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function created_at($value) | ||||
|     public function created_at($value = '') | ||||
|     { | ||||
|         $created_at = $value ? (int) $value : 0; | ||||
|          | ||||
|         $created_at = date('Y-m-d H:i:s', $value); | ||||
|         if($value == '') | ||||
|             return $this->builder; | ||||
| 
 | ||||
|         if(is_string($created_at)){ | ||||
|         try{ | ||||
| 
 | ||||
|             $created_at = strtotime(str_replace("/","-",$created_at)); | ||||
|             if(is_numeric($value)){ | ||||
|                 $created_at = Carbon::createFromTimestamp((int)$value); | ||||
|             } | ||||
|             else{ | ||||
|                 $created_at = Carbon::parse($value); | ||||
|             } | ||||
| 
 | ||||
|             if(!$created_at) | ||||
|                 return $this->builder; | ||||
|             return $this->builder->where('created_at', '>=', $created_at); | ||||
| 
 | ||||
|         } | ||||
|         catch(\Exception $e) { | ||||
| 
 | ||||
|             return $this->builder; | ||||
| 
 | ||||
|         } | ||||
|          | ||||
|         return $this->builder->where('created_at', '>=', $created_at); | ||||
|     } | ||||
| 
 | ||||
|     public function is_deleted($value) | ||||
| @ -209,6 +218,15 @@ abstract class QueryFilters | ||||
|         return $this->builder->where('client_id', $this->decodePrimaryKey($client_id)); | ||||
|     } | ||||
| 
 | ||||
|     public function vendor_id(string $vendor_id = '') :Builder | ||||
|     { | ||||
|         if (strlen($vendor_id) == 0) { | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         return $this->builder->where('vendor_id', $this->decodePrimaryKey($vendor_id)); | ||||
|     } | ||||
| 
 | ||||
|     public function filter_deleted_clients($value) | ||||
|     { | ||||
|         if ($value == 'true') { | ||||
| @ -226,12 +244,6 @@ abstract class QueryFilters | ||||
|             return $this->builder->where('is_deleted', 0); | ||||
|         } | ||||
| 
 | ||||
|         // if($value == 'true'){
 | ||||
| 
 | ||||
|         //     $this->builder->withTrashed();
 | ||||
| 
 | ||||
|         // }
 | ||||
| 
 | ||||
|         return $this->builder; | ||||
|     } | ||||
| 
 | ||||
| @ -239,6 +251,7 @@ abstract class QueryFilters | ||||
|     { | ||||
|         return $this->builder | ||||
|             ->orWhere($this->with_property, $value) | ||||
|             ->orderByRaw("{$this->with_property} = ? DESC", [$value]); | ||||
|             ->orderByRaw("{$this->with_property} = ? DESC", [$value]) | ||||
|             ->company(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -66,27 +66,40 @@ class QuoteFilters extends QueryFilters | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         $quote_filters = []; | ||||
| 
 | ||||
|         if (in_array('draft', $status_parameters)) { | ||||
|             $this->builder->where('status_id', Quote::STATUS_DRAFT); | ||||
|             $quote_filters[] = Quote::STATUS_DRAFT; | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('sent', $status_parameters)) { | ||||
|             $this->builder->where('status_id', Quote::STATUS_SENT); | ||||
|             $quote_filters[] = Quote::STATUS_SENT; | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('approved', $status_parameters)) { | ||||
|             $this->builder->where('status_id', Quote::STATUS_APPROVED); | ||||
|             $quote_filters[] = Quote::STATUS_APPROVED; | ||||
|         } | ||||
| 
 | ||||
|         if(count($quote_filters) >=1){ | ||||
|             $this->builder->whereIn('status_id', $quote_filters); | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('expired', $status_parameters)) { | ||||
|             $this->builder->where('status_id', Quote::STATUS_SENT) | ||||
|                           ->where('due_date', '>=', now()->toDateString()); | ||||
|             $this->builder->orWhere(function ($query){ | ||||
|                           $query->where('status_id', Quote::STATUS_SENT) | ||||
|                           ->company() | ||||
|                           ->whereNotNull('due_date') | ||||
|                           ->where('due_date', '<=', now()->toDateString()); | ||||
|                       }); | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('upcoming', $status_parameters)) { | ||||
|             $this->builder->where('status_id', Quote::STATUS_SENT) | ||||
|                           ->where('due_date', '<=', now()->toDateString()) | ||||
|             $this->builder->orWhere(function ($query){ | ||||
|                         $query->where('status_id', Quote::STATUS_SENT) | ||||
|                           ->company() | ||||
|                           ->where('due_date', '>=', now()->toDateString()) | ||||
|                           ->orderBy('due_date', 'DESC'); | ||||
|                       }); | ||||
|         } | ||||
| 
 | ||||
|         return $this->builder; | ||||
|  | ||||
							
								
								
									
										129
									
								
								app/Filters/SubscriptionFilters.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								app/Filters/SubscriptionFilters.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,129 @@ | ||||
| <?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\Filters; | ||||
| 
 | ||||
| use App\Models\User; | ||||
| use App\Models\Webhook; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Support\Facades\DB; | ||||
| use Illuminate\Support\Facades\Gate; | ||||
| 
 | ||||
| /** | ||||
|  * SubscriptionFilters. | ||||
|  */ | ||||
| class SubscriptionFilters extends QueryFilters | ||||
| { | ||||
|     /** | ||||
|      * Filter based on search text. | ||||
|      * | ||||
|      * @param string query filter | ||||
|      * @return Builder | ||||
|      * @deprecated | ||||
|      */ | ||||
|     public function filter(string $filter = '') : Builder | ||||
|     { | ||||
|         if (strlen($filter) == 0) { | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         return  $this->builder->where(function ($query) use ($filter) { | ||||
|             $query->where('name', 'like', '%'.$filter.'%'); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Filters the list based on the status | ||||
|      * archived, active, deleted. | ||||
|      * | ||||
|      * @param string filter | ||||
|      * @return Builder | ||||
|      */ | ||||
|     public function status(string $filter = '') : Builder | ||||
|     { | ||||
|         if (strlen($filter) == 0) { | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         $table = 'subscriptions'; | ||||
|         $filters = explode(',', $filter); | ||||
| 
 | ||||
|         return $this->builder->where(function ($query) use ($filters, $table) { | ||||
|             $query->whereNull($table.'.id'); | ||||
| 
 | ||||
|             if (in_array(parent::STATUS_ACTIVE, $filters)) { | ||||
|                 $query->orWhereNull($table.'.deleted_at'); | ||||
|             } | ||||
| 
 | ||||
|             if (in_array(parent::STATUS_ARCHIVED, $filters)) { | ||||
|                 $query->orWhere(function ($query) use ($table) { | ||||
|                     $query->whereNotNull($table.'.deleted_at'); | ||||
| 
 | ||||
|                     if (! in_array($table, ['users'])) { | ||||
|                         $query->where($table.'.is_deleted', '=', 0); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|             if (in_array(parent::STATUS_DELETED, $filters)) { | ||||
|                 $query->orWhere($table.'.is_deleted', '=', 1); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sorts the list based on $sort. | ||||
|      * | ||||
|      * @param string sort formatted as column|asc | ||||
|      * @return Builder | ||||
|      */ | ||||
|     public function sort(string $sort) : Builder | ||||
|     { | ||||
|         $sort_col = explode('|', $sort); | ||||
| 
 | ||||
|         return $this->builder->orderBy($sort_col[0], $sort_col[1]); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the base query. | ||||
|      * | ||||
|      * @param int company_id | ||||
|      * @param User $user | ||||
|      * @return Builder | ||||
|      * @deprecated | ||||
|      */ | ||||
|     public function baseQuery(int $company_id, User $user) : Builder | ||||
|     { | ||||
|         $query = DB::table('subscriptions') | ||||
|             ->join('companies', 'companies.id', '=', 'subscriptions.company_id') | ||||
|             ->where('subscriptions.company_id', '=', $company_id); | ||||
| 
 | ||||
|         /* | ||||
|          * If the user does not have permissions to view all invoices | ||||
|          * limit the user to only the invoices they have created | ||||
|          */ | ||||
|         if (Gate::denies('view-list', Webhook::class)) { | ||||
|             $query->where('subscriptions.user_id', '=', $user->id); | ||||
|         } | ||||
| 
 | ||||
|         return $query; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Filters the query by the users company ID. | ||||
|      * | ||||
|      * @return Illuminate\Database\Query\Builder | ||||
|      */ | ||||
|     public function entityFilter() | ||||
|     { | ||||
|         return $this->builder->company(); | ||||
|     } | ||||
| } | ||||
| @ -104,7 +104,6 @@ class TokenFilters extends QueryFilters | ||||
|         $query = DB::table('company_tokens') | ||||
|             ->join('companies', 'companies.id', '=', 'company_tokens.company_id') | ||||
|             ->where('company_tokens.company_id', '=', $company_id) | ||||
|             //->whereRaw('(designs.name != "" or contacts.first_name != "" or contacts.last_name != "" or contacts.email != "")') // filter out buy now invoices
 | ||||
|             ->select( | ||||
|                 'company_tokens.id', | ||||
|                 'company_tokens.name', | ||||
|  | ||||
| @ -118,7 +118,6 @@ class VendorFilters extends QueryFilters | ||||
|             ->where('vendors.company_id', '=', $company_id) | ||||
|             ->where('vendor_contacts.is_primary', '=', true) | ||||
|             ->where('vendor_contacts.deleted_at', '=', null) | ||||
|             //->whereRaw('(vendors.name != "" or contacts.first_name != "" or contacts.last_name != "" or contacts.email != "")') // filter out buy now invoices
 | ||||
|             ->select( | ||||
|                // DB::raw('COALESCE(vendors.currency_id, companies.currency_id) currency_id'),
 | ||||
|                 DB::raw('COALESCE(vendors.country_id, companies.country_id) country_id'), | ||||
|  | ||||
| @ -105,7 +105,6 @@ class WebhookFilters extends QueryFilters | ||||
|         $query = DB::table('webhooks') | ||||
|             ->join('companies', 'companies.id', '=', 'webhooks.company_id') | ||||
|             ->where('webhooks.company_id', '=', $company_id) | ||||
|             //->whereRaw('(designs.name != "" or contacts.first_name != "" or contacts.last_name != "" or contacts.email != "")') // filter out buy now invoices
 | ||||
|             ->select( | ||||
|                 'webhooks.id', | ||||
|                 'webhooks.target_url', | ||||
|  | ||||
| @ -13,6 +13,7 @@ namespace App\Helpers\Epc; | ||||
| 
 | ||||
| use App\Models\Company; | ||||
| use App\Models\Invoice; | ||||
| use App\Models\RecurringInvoice; | ||||
| use App\Utils\Ninja; | ||||
| use BaconQrCode\Renderer\ImageRenderer; | ||||
| use BaconQrCode\Renderer\Image\SvgImageBackEnd; | ||||
| @ -35,7 +36,7 @@ class EpcQrGenerator | ||||
| 
 | ||||
|     ]; | ||||
| 
 | ||||
|     public function __construct(protected Company $company, protected Invoice $invoice, protected float $amount){} | ||||
|     public function __construct(protected Company $company, protected Invoice|RecurringInvoice $invoice, protected float $amount){} | ||||
| 
 | ||||
|     public function getQrCode() | ||||
|     { | ||||
|  | ||||
| @ -87,10 +87,10 @@ class SwissQrGenerator | ||||
|     $qrBill->setUltimateDebtor( | ||||
|         QrBill\DataGroup\Element\StructuredAddress::createWithStreet( | ||||
|             substr($this->client->present()->name(), 0 , 70), | ||||
|             $this->client->address1 ? substr($this->client->address1, 0 , 70) : '_', | ||||
|             $this->client->address2 ? substr($this->client->address2, 0 , 16) : '_', | ||||
|             $this->client->postal_code ? substr($this->client->postal_code, 0, 16) : '_', | ||||
|             $this->client->city ? substr($this->client->city, 0, 35) : '_', | ||||
|             $this->client->address1 ? substr($this->client->address1, 0 , 70) : ' ', | ||||
|             $this->client->address2 ? substr($this->client->address2, 0 , 16) : ' ', | ||||
|             $this->client->postal_code ? substr($this->client->postal_code, 0, 16) : ' ', | ||||
|             $this->client->city ? substr($this->client->city, 0, 35) : ' ', | ||||
|             'CH' | ||||
|         )); | ||||
| 
 | ||||
|  | ||||
| @ -112,6 +112,7 @@ class ActivityController extends BaseController | ||||
|                     'purchase_order' => $activity->purchase_order ? $activity->purchase_order : '', | ||||
|                     'subscription' => $activity->subscription ? $activity->subscription : '', | ||||
|                     'vendor_contact' => $activity->vendor_contact ? $activity->vendor_contact : '', | ||||
|                     'recurring_expense' => $activity->recurring_expense ? $activity->recurring_expense : '', | ||||
|                 ]; | ||||
| 
 | ||||
|                 return array_merge($arr, $activity->toArray()); | ||||
|  | ||||
| @ -51,6 +51,7 @@ class ContactRegisterController extends Controller | ||||
| 
 | ||||
|     public function register(RegisterRequest $request) | ||||
|     { | ||||
| 
 | ||||
|         $request->merge(['company' => $request->company()]); | ||||
| 
 | ||||
|         $client = $this->getClient($request->all()); | ||||
| @ -58,7 +59,7 @@ class ContactRegisterController extends Controller | ||||
| 
 | ||||
|         Auth::guard('contact')->loginUsingId($client_contact->id, true); | ||||
| 
 | ||||
|         return redirect()->route('client.dashboard'); | ||||
|         return redirect()->intended(route('client.dashboard')); | ||||
|     } | ||||
| 
 | ||||
|     private function getClient(array $data) | ||||
| @ -66,7 +67,15 @@ class ContactRegisterController extends Controller | ||||
|         $client = ClientFactory::create($data['company']->id, $data['company']->owner()->id); | ||||
| 
 | ||||
|         $client->fill($data); | ||||
| 
 | ||||
|         $client->save(); | ||||
| 
 | ||||
|         if(isset($data['currency_id'])) { | ||||
|             $settings = $client->settings; | ||||
|             $settings->currency_id = isset($data['currency_id']) ? $data['currency_id'] : $data['company']->settings->currency_id; | ||||
|             $client->settings = $settings; | ||||
|         } | ||||
| 
 | ||||
|         $client->number = $this->getNextClientNumber($client); | ||||
|         $client->save(); | ||||
| 
 | ||||
|  | ||||
| @ -984,6 +984,9 @@ class BaseController extends Controller | ||||
|             //pass report errors bool to front end
 | ||||
|             $data['report_errors'] = Ninja::isSelfHost() ? $account->report_errors : true; | ||||
| 
 | ||||
|             //pass whitelabel bool to front end
 | ||||
|             $data['white_label'] = Ninja::isSelfHost() ? $account->isPaid() : false; | ||||
| 
 | ||||
|             //pass referral code to front end
 | ||||
|             $data['rc'] = request()->has('rc') ? request()->input('rc') : ''; | ||||
|             $data['build'] = request()->has('build') ? request()->input('build') : ''; | ||||
|  | ||||
| @ -52,7 +52,7 @@ class InvoiceController extends Controller | ||||
|      * | ||||
|      * @return Factory|View | ||||
|      */ | ||||
|     public function show(ShowInvoiceRequest $request, Invoice $invoice) | ||||
|     public function show(ShowInvoiceRequest $request, Invoice $invoice, ?string $hash = null) | ||||
|     { | ||||
|         set_time_limit(0); | ||||
| 
 | ||||
| @ -69,6 +69,7 @@ class InvoiceController extends Controller | ||||
|             'invoice' => $invoice, | ||||
|             'invitation' => $invitation ?: $invoice->invitations->first(), | ||||
|             'key' => $invitation ? $invitation->key : false, | ||||
|             'hash' => $hash, | ||||
|         ]; | ||||
| 
 | ||||
|         if ($request->query('mode') === 'fullscreen') { | ||||
|  | ||||
| @ -26,6 +26,7 @@ use App\Models\Invoice; | ||||
| use App\Models\RecurringInvoice; | ||||
| use App\Models\Subscription; | ||||
| use App\Notifications\Ninja\NewAccountNotification; | ||||
| use App\Repositories\RecurringInvoiceRepository; | ||||
| use App\Repositories\SubscriptionRepository; | ||||
| use App\Utils\Ninja; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| @ -147,6 +148,7 @@ class NinjaPlanController extends Controller | ||||
|             $account->plan_term = 'month'; | ||||
|             $account->plan_started = now(); | ||||
|             $account->plan_expires = now()->addDays(14); | ||||
|             $account->is_trial=true; | ||||
|             $account->save(); | ||||
|         } | ||||
| 
 | ||||
| @ -178,6 +180,15 @@ class NinjaPlanController extends Controller | ||||
|                  ->increment() | ||||
|                  ->queue(); | ||||
| 
 | ||||
| 
 | ||||
|         $old_recurring = RecurringInvoice::where('company_id', config('ninja.ninja_default_company_id'))->where('client_id', $client->id)->first(); | ||||
| 
 | ||||
|         if($old_recurring) { | ||||
|             $old_recurring->service()->stop()->save(); | ||||
|             $old_recurring_repo = new RecurringInvoiceRepository(); | ||||
|             $old_recurring_repo->archive($old_recurring); | ||||
|         } | ||||
| 
 | ||||
|         $ninja_company = Company::on('db-ninja-01')->find(config('ninja.ninja_default_company_id')); | ||||
|         $ninja_company->notification(new NewAccountNotification($subscription->company->account, $client))->ninja(); | ||||
| 
 | ||||
| @ -206,7 +217,7 @@ class NinjaPlanController extends Controller | ||||
| 
 | ||||
|             if ($account) { | ||||
|                 //offer the option to have a free trial
 | ||||
|                 if (! $account->trial_started && ! $account->plan) { | ||||
|                 if (!$account->is_trial) { | ||||
|                     return $this->trial(); | ||||
|                 } | ||||
| 
 | ||||
|  | ||||
| @ -148,8 +148,17 @@ class PaymentController extends Controller | ||||
| 
 | ||||
|         $payment = $payment->service()->applyCredits($payment_hash)->save(); | ||||
| 
 | ||||
|         $invoices = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id'))); | ||||
| 
 | ||||
|         event('eloquent.created: App\Models\Payment', $payment); | ||||
| 
 | ||||
|         if($invoices->sum('balance') > 0){ | ||||
| 
 | ||||
|             $invoice = $invoices->first(); | ||||
| 
 | ||||
|             return redirect()->route('client.invoice.show', ['invoice' => $invoice->hashed_id, 'hash' => $request->input('hash')]); | ||||
|         } | ||||
| 
 | ||||
|         if (property_exists($payment_hash->data, 'billing_context')) { | ||||
|             $billing_subscription = \App\Models\Subscription::find($payment_hash->data->billing_context->subscription_id); | ||||
| 
 | ||||
|  | ||||
| @ -13,6 +13,7 @@ | ||||
| namespace App\Http\Controllers\ClientPortal; | ||||
| 
 | ||||
| use App\Http\Controllers\Controller; | ||||
| use App\Http\Requests\ClientPortal\RecurringInvoices\ShowRecurringInvoiceRequest; | ||||
| use App\Models\RecurringInvoice; | ||||
| use App\Utils\Ninja; | ||||
| use Illuminate\Http\Request; | ||||
| @ -38,4 +39,20 @@ class SubscriptionController extends Controller | ||||
| 
 | ||||
|         return render('subscriptions.index'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Display the recurring invoice. | ||||
|      * | ||||
|      * @param ShowRecurringInvoiceRequest $request | ||||
|      * @param RecurringInvoice $recurring_invoice | ||||
|      * @return Factory|View | ||||
|      */ | ||||
|     public function show(ShowRecurringInvoiceRequest $request, RecurringInvoice $recurring_invoice) | ||||
|     { | ||||
|         return $this->render('subscriptions.show', [ | ||||
|             'invoice' => $recurring_invoice->load('invoices','subscription'), | ||||
|             'subscription' => $recurring_invoice->subscription | ||||
|         ]); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -31,9 +31,12 @@ class SubscriptionPlanSwitchController extends Controller | ||||
|      */ | ||||
|     public function index(ShowPlanSwitchRequest $request, RecurringInvoice $recurring_invoice, Subscription $target) | ||||
|     { | ||||
| 
 | ||||
|         $amount = $recurring_invoice->subscription | ||||
|                                     ->service() | ||||
|                                     ->calculateUpgradePrice($recurring_invoice, $target); | ||||
|                                     ->calculateUpgradePriceV2($recurring_invoice, $target); | ||||
| 
 | ||||
|         nlog("payment amount = {$amount}"); | ||||
|         /** | ||||
|          * Null value here is a proxy for | ||||
|          * denying the user a change plan option | ||||
| @ -42,11 +45,14 @@ class SubscriptionPlanSwitchController extends Controller | ||||
|             render('subscriptions.denied'); | ||||
|         } | ||||
| 
 | ||||
|         $amount = max(0,$amount); | ||||
| 
 | ||||
|         return render('subscriptions.switch', [ | ||||
|             'subscription' => $recurring_invoice->subscription, | ||||
|             'recurring_invoice' => $recurring_invoice, | ||||
|             'target' => $target, | ||||
|             'amount' => $amount, | ||||
|         ]); | ||||
|          | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -53,6 +53,16 @@ class SubscriptionPurchaseController extends Controller | ||||
|             $this->setLocale($request->query('locale')); | ||||
|         } | ||||
| 
 | ||||
|         if(!auth()->guard('contact')->check() && $subscription->registration_required && $subscription->company->client_can_register) { | ||||
| 
 | ||||
|             session()->put('url.intended', route('client.subscription.upgrade',['subscription' => $subscription->hashed_id])); | ||||
| 
 | ||||
|             return redirect()->route('client.register', ['company_key' => $subscription->company->company_key]); | ||||
|         } | ||||
|         elseif(!auth()->guard('contact')->check() && $subscription->registration_required && ! $subscription->company->client_can_register) { | ||||
|             return render('generic.subscription_blocked', ['account' => $subscription->company->account, 'company' => $subscription->company]); | ||||
|         } | ||||
| 
 | ||||
|         return view('billing-portal.purchasev2', [ | ||||
|             'subscription' => $subscription, | ||||
|             'hash' => Str::uuid()->toString(), | ||||
|  | ||||
| @ -521,7 +521,7 @@ class CompanyController extends BaseController | ||||
|             $nmo->company = $other_company; | ||||
|             $nmo->settings = $other_company->settings; | ||||
|             $nmo->to_user = auth()->user(); | ||||
|             NinjaMailerJob::dispatch($nmo, true); | ||||
|             (new NinjaMailerJob($nmo, true))->handle(); | ||||
| 
 | ||||
|             $company->delete(); | ||||
| 
 | ||||
|  | ||||
| @ -12,12 +12,14 @@ | ||||
| namespace App\Http\Controllers; | ||||
| 
 | ||||
| use App\Factory\ExpenseCategoryFactory; | ||||
| use App\Filters\ExpenseCategoryFilters; | ||||
| use App\Http\Requests\ExpenseCategory\CreateExpenseCategoryRequest; | ||||
| use App\Http\Requests\ExpenseCategory\DestroyExpenseCategoryRequest; | ||||
| use App\Http\Requests\ExpenseCategory\EditExpenseCategoryRequest; | ||||
| use App\Http\Requests\ExpenseCategory\ShowExpenseCategoryRequest; | ||||
| use App\Http\Requests\ExpenseCategory\StoreExpenseCategoryRequest; | ||||
| use App\Http\Requests\ExpenseCategory\UpdateExpenseCategoryRequest; | ||||
| use App\Models\Expense; | ||||
| use App\Models\ExpenseCategory; | ||||
| use App\Repositories\BaseRepository; | ||||
| use App\Transformers\ExpenseCategoryTransformer; | ||||
| @ -79,13 +81,15 @@ class ExpenseCategoryController extends BaseController | ||||
|      * | ||||
|      * @return Response | ||||
|      */ | ||||
|     public function index() | ||||
|     public function index(ExpenseCategoryFilters $filters) | ||||
|     { | ||||
|         $expense_categories = ExpenseCategory::scope(); | ||||
|         $expense_categories = ExpenseCategory::filter($filters); | ||||
| 
 | ||||
|         return $this->listResponse($expense_categories); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Show the form for creating a new resource. | ||||
|      * | ||||
| @ -131,11 +135,45 @@ class ExpenseCategoryController extends BaseController | ||||
|         return $this->itemResponse($expense_category); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Store a newly created resource in storage. | ||||
|      * | ||||
|      * @param StoreExpenseCategoryRequest $request | ||||
|      * @param StoreInvoiceRequest $request  The request | ||||
|      * | ||||
|      * @return Response | ||||
|      * | ||||
|      * | ||||
|      * @OA\Post( | ||||
|      *      path="/api/v1/expense_categories", | ||||
|      *      operationId="storeExpenseCategory", | ||||
|      *      tags={"expense_categories"}, | ||||
|      *      summary="Adds a expense category", | ||||
|      *      description="Adds an expense category to the system", | ||||
|      *      @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), | ||||
|      *      @OA\Parameter(ref="#/components/parameters/X-Api-Token"), | ||||
|      *      @OA\Parameter(ref="#/components/parameters/X-Requested-With"), | ||||
|      *      @OA\Parameter(ref="#/components/parameters/include"), | ||||
|      *      @OA\Response( | ||||
|      *          response=200, | ||||
|      *          description="Returns the saved invoice object", | ||||
|      *          @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), | ||||
|      *          @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), | ||||
|      *          @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), | ||||
|      *          @OA\JsonContent(ref="#/components/schemas/ExpenseCategory"), | ||||
|      *       ), | ||||
|      *       @OA\Response( | ||||
|      *          response=422, | ||||
|      *          description="Validation error", | ||||
|      *          @OA\JsonContent(ref="#/components/schemas/ValidationError"), | ||||
|      * | ||||
|      *       ), | ||||
|      *       @OA\Response( | ||||
|      *           response="default", | ||||
|      *           description="Unexpected Error", | ||||
|      *           @OA\JsonContent(ref="#/components/schemas/Error"), | ||||
|      *       ), | ||||
|      *     ) | ||||
|      */ | ||||
|     public function store(StoreExpenseCategoryRequest $request) | ||||
|     { | ||||
|  | ||||
| @ -13,7 +13,6 @@ namespace App\Http\Controllers; | ||||
| 
 | ||||
| use App\Http\Requests\Import\ImportRequest; | ||||
| use App\Http\Requests\Import\PreImportRequest; | ||||
| use App\Jobs\Import\CSVImport; | ||||
| use App\Jobs\Import\CSVIngest; | ||||
| use Illuminate\Http\UploadedFile; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
| @ -162,7 +161,7 @@ class ImportController extends Controller | ||||
|     public function detectDelimiter($csvfile) | ||||
|     { | ||||
|         $delimiters = array(',', '.', ';'); | ||||
|         $bestDelimiter = false; | ||||
|         $bestDelimiter = ' '; | ||||
|         $count = 0; | ||||
|         foreach ($delimiters as $delimiter) | ||||
|             if (substr_count($csvfile, $delimiter) > $count) { | ||||
|  | ||||
| @ -781,7 +781,8 @@ class InvoiceController extends BaseController | ||||
|             case 'email': | ||||
|                 //check query parameter for email_type and set the template else use calculateTemplate
 | ||||
| 
 | ||||
|                 if (request()->has('email_type') && in_array(request()->input('email_type'), ['reminder1', 'reminder2', 'reminder3', 'reminder_endless', 'custom1', 'custom2', 'custom3'])) { | ||||
|                 // if (request()->has('email_type') && in_array(request()->input('email_type'), ['reminder1', 'reminder2', 'reminder3', 'reminder_endless', 'custom1', 'custom2', 'custom3'])) {
 | ||||
|                 if (request()->has('email_type') && property_exists($invoice->company->settings, request()->input('email_type'))) { | ||||
|                     $this->reminder_template = $invoice->client->getSetting(request()->input('email_type')); | ||||
|                 } else { | ||||
|                     $this->reminder_template = $invoice->calculateTemplate('invoice'); | ||||
|  | ||||
| @ -18,6 +18,10 @@ class PaymentWebhookController extends Controller | ||||
| { | ||||
|     public function __invoke(PaymentWebhookRequest $request) | ||||
|     { | ||||
|         //return early if we cannot resolve the company gateway
 | ||||
|         if(!$request->getCompanyGateway()) | ||||
|             return response()->json([], 200); | ||||
| 
 | ||||
|         return $request | ||||
|             ->getCompanyGateway() | ||||
|             ->driver() | ||||
|  | ||||
| @ -74,7 +74,7 @@ class PostMarkController extends BaseController | ||||
|      */ | ||||
|     public function webhook(Request $request) | ||||
|     { | ||||
|         if ($request->header('X-API-SECURITY') && $request->header('X-API-SECURITY') == config('postmark.secret')) { | ||||
|         if ($request->header('X-API-SECURITY') && $request->header('X-API-SECURITY') == config('services.postmark.token')) { | ||||
|             ProcessPostmarkWebhook::dispatch($request->all()); | ||||
| 
 | ||||
|             return response()->json(['message' => 'Success'], 200); | ||||
|  | ||||
| @ -198,7 +198,7 @@ class PurchaseOrderController extends BaseController | ||||
|              | ||||
|         event(new PurchaseOrderWasCreated($purchase_order, $purchase_order->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); | ||||
| 
 | ||||
|         return $this->itemResponse($purchase_order); | ||||
|         return $this->itemResponse($purchase_order->fresh()); | ||||
|     } | ||||
|     /** | ||||
|      * Display the specified resource. | ||||
| @ -502,7 +502,6 @@ class PurchaseOrderController extends BaseController | ||||
|         /* | ||||
|          * Download Purchase Order/s | ||||
|          */ | ||||
| 
 | ||||
|         if ($action == 'bulk_download' && $purchase_orders->count() >= 1) { | ||||
|             $purchase_orders->each(function ($purchase_order) { | ||||
|                 if (auth()->user()->cannot('view', $purchase_order)) { | ||||
|  | ||||
| @ -0,0 +1,89 @@ | ||||
| <?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\Http\Controllers\Reports; | ||||
| 
 | ||||
| use App\Export\CSV\ProductExport; | ||||
| use App\Export\CSV\ProductSalesExport; | ||||
| use App\Http\Controllers\BaseController; | ||||
| use App\Http\Requests\Report\GenericReportRequest; | ||||
| use App\Http\Requests\Report\ProductSalesReportRequest; | ||||
| use App\Jobs\Report\SendToAdmin; | ||||
| use App\Models\Client; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use Illuminate\Http\Response; | ||||
| 
 | ||||
| class ProductSalesReportController extends BaseController | ||||
| { | ||||
|     use MakesHash; | ||||
| 
 | ||||
|     private string $filename = 'product_sales.csv'; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @OA\Post( | ||||
|      *      path="/api/v1/reports/product_sales", | ||||
|      *      operationId="getProductSalesReport", | ||||
|      *      tags={"reports"}, | ||||
|      *      summary="Product Salesreports", | ||||
|      *      description="Export product sales reports", | ||||
|      *      @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), | ||||
|      *      @OA\Parameter(ref="#/components/parameters/X-Requested-With"), | ||||
|      *      @OA\RequestBody( | ||||
|      *          required=true, | ||||
|      *          @OA\JsonContent(ref="#/components/schemas/GenericReportSchema") | ||||
|      *      ), | ||||
|      *      @OA\Response( | ||||
|      *          response=200, | ||||
|      *          description="success", | ||||
|      *          @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), | ||||
|      *          @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), | ||||
|      *          @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), | ||||
|      *       ), | ||||
|      *       @OA\Response( | ||||
|      *          response=422, | ||||
|      *          description="Validation error", | ||||
|      *          @OA\JsonContent(ref="#/components/schemas/ValidationError"), | ||||
|      *       ), | ||||
|      *       @OA\Response( | ||||
|      *           response="default", | ||||
|      *           description="Unexpected Error", | ||||
|      *           @OA\JsonContent(ref="#/components/schemas/Error"), | ||||
|      *       ), | ||||
|      *     ) | ||||
|      */ | ||||
|     public function __invoke(ProductSalesReportRequest $request) | ||||
|     { | ||||
|         if ($request->has('send_email') && $request->get('send_email')) { | ||||
|             SendToAdmin::dispatch(auth()->user()->company(), $request->all(), ProductSalesExport::class, $this->filename); | ||||
| 
 | ||||
|             return response()->json(['message' => 'working...'], 200); | ||||
|         } | ||||
|         // expect a list of visible fields, or use the default
 | ||||
| 
 | ||||
|         $export = new ProductSalesExport(auth()->user()->company(), $request->all()); | ||||
| 
 | ||||
|         $csv = $export->run(); | ||||
| 
 | ||||
|         $headers = [ | ||||
|             'Content-Disposition' => 'attachment', | ||||
|             'Content-Type' => 'text/csv', | ||||
|         ]; | ||||
| 
 | ||||
|         return response()->streamDownload(function () use ($csv) { | ||||
|             echo $csv; | ||||
|         }, $this->filename, $headers); | ||||
|     } | ||||
| } | ||||
| @ -1,4 +1,13 @@ | ||||
| <?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\Http\Controllers; | ||||
| 
 | ||||
|  | ||||
| @ -15,6 +15,7 @@ namespace App\Http\Controllers; | ||||
| use App\Events\Subscription\SubscriptionWasCreated; | ||||
| use App\Events\Subscription\SubscriptionWasUpdated; | ||||
| use App\Factory\SubscriptionFactory; | ||||
| use App\Filters\SubscriptionFilters; | ||||
| use App\Http\Requests\Subscription\CreateSubscriptionRequest; | ||||
| use App\Http\Requests\Subscription\DestroySubscriptionRequest; | ||||
| use App\Http\Requests\Subscription\EditSubscriptionRequest; | ||||
| @ -80,9 +81,9 @@ class SubscriptionController extends BaseController | ||||
|      *       ), | ||||
|      *     ) | ||||
|      */ | ||||
|     public function index(): \Illuminate\Http\Response | ||||
|     public function index(SubscriptionFilters $filters): \Illuminate\Http\Response | ||||
|     { | ||||
|         $subscriptions = Subscription::query()->company(); | ||||
|         $subscriptions = Subscription::filter($filters); | ||||
| 
 | ||||
|         return $this->listResponse($subscriptions); | ||||
|     } | ||||
|  | ||||
| @ -11,37 +11,40 @@ | ||||
| 
 | ||||
| namespace App\Http\Controllers; | ||||
| 
 | ||||
| use App\Http\Requests\TaskScheduler\CreateScheduledTaskRequest; | ||||
| use App\Http\Requests\TaskScheduler\UpdateScheduleRequest; | ||||
| use App\Factory\SchedulerFactory; | ||||
| use App\Http\Requests\TaskScheduler\CreateSchedulerRequest; | ||||
| use App\Http\Requests\TaskScheduler\ShowSchedulerRequest; | ||||
| use App\Http\Requests\TaskScheduler\StoreSchedulerRequest; | ||||
| use App\Http\Requests\TaskScheduler\UpdateSchedulerRequest; | ||||
| use App\Http\Requests\Task\DestroySchedulerRequest; | ||||
| use App\Jobs\Ninja\TaskScheduler; | ||||
| use App\Jobs\Report\ProfitAndLoss; | ||||
| use App\Models\Scheduler; | ||||
| use App\Repositories\TaskSchedulerRepository; | ||||
| use App\Transformers\TaskSchedulerTransformer; | ||||
| use App\Repositories\SchedulerRepository; | ||||
| use App\Transformers\SchedulerTransformer; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use Carbon\Carbon; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
| 
 | ||||
| class TaskSchedulerController extends BaseController | ||||
| { | ||||
|     use MakesHash; | ||||
| 
 | ||||
|     protected $entity_type = Scheduler::class; | ||||
| 
 | ||||
|     protected $entity_transformer = TaskSchedulerTransformer::class; | ||||
|     protected $entity_transformer = SchedulerTransformer::class; | ||||
| 
 | ||||
|     protected TaskSchedulerRepository $scheduler_repository; | ||||
| 
 | ||||
|     public function __construct(TaskSchedulerRepository $scheduler_repository) | ||||
|     public function __construct(protected SchedulerRepository $scheduler_repository) | ||||
|     { | ||||
|         parent::__construct(); | ||||
| 
 | ||||
|         $this->scheduler_repository = $scheduler_repository; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @OA\GET( | ||||
|      *      path="/api/v1/task_scheduler/", | ||||
|      *      path="/api/v1/task_schedulers/", | ||||
|      *      operationId="getTaskSchedulers", | ||||
|      *      tags={"task_scheduler"}, | ||||
|      *      tags={"task_schedulers"}, | ||||
|      *      summary="Task Scheduler Index", | ||||
|      *      description="Get all schedulers with associated jobs", | ||||
|      *      @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), | ||||
| @ -67,11 +70,57 @@ class TaskSchedulerController extends BaseController | ||||
|         return $this->listResponse($schedulers); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Show the form for creating a new resource. | ||||
|      * | ||||
|      * @param CreateSchedulerRequest $request  The request | ||||
|      * | ||||
|      * @return Response | ||||
|      * | ||||
|      * | ||||
|      * @OA\Get( | ||||
|      *      path="/api/v1/invoices/task_schedulers", | ||||
|      *      operationId="getTaskScheduler", | ||||
|      *      tags={"task_schedulers"}, | ||||
|      *      summary="Gets a new blank scheduler object", | ||||
|      *      description="Returns a blank object with default values", | ||||
|      *      @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), | ||||
|      *      @OA\Parameter(ref="#/components/parameters/X-Api-Token"), | ||||
|      *      @OA\Parameter(ref="#/components/parameters/X-Requested-With"), | ||||
|      *      @OA\Parameter(ref="#/components/parameters/include"), | ||||
|      *      @OA\Response( | ||||
|      *          response=200, | ||||
|      *          description="A blank scheduler object", | ||||
|      *          @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), | ||||
|      *          @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), | ||||
|      *          @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), | ||||
|      *          @OA\JsonContent(ref="#/components/schemas/TaskSchedulerSchema"), | ||||
|      *       ), | ||||
|      *       @OA\Response( | ||||
|      *          response=422, | ||||
|      *          description="Validation error", | ||||
|      *          @OA\JsonContent(ref="#/components/schemas/ValidationError"), | ||||
|      * | ||||
|      *       ), | ||||
|      *       @OA\Response( | ||||
|      *           response="default", | ||||
|      *           description="Unexpected Error", | ||||
|      *           @OA\JsonContent(ref="#/components/schemas/Error"), | ||||
|      *       ), | ||||
|      *     ) | ||||
|      */ | ||||
|     public function create(CreateSchedulerRequest $request) | ||||
|     { | ||||
|         $scheduler = SchedulerFactory::create(auth()->user()->company()->id, auth()->user()->id); | ||||
| 
 | ||||
|         return $this->itemResponse($scheduler); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @OA\Post( | ||||
|      *      path="/api/v1/task_scheduler/", | ||||
|      *      path="/api/v1/task_schedulers/", | ||||
|      *      operationId="createTaskScheduler", | ||||
|      *      tags={"task_scheduler"}, | ||||
|      *      tags={"task_schedulers"}, | ||||
|      *      summary="Create task scheduler with job ", | ||||
|      *      description="Create task scheduler with a job (action(job) request should be sent via request also. Example: We want client report to be job which will be run
 | ||||
|      * multiple times, we should send the same parameters in the request as we would send if we wanted to get report, see example",
 | ||||
| @ -100,19 +149,18 @@ class TaskSchedulerController extends BaseController | ||||
|      *       ), | ||||
|      *     ) | ||||
|      */ | ||||
|     public function store(CreateScheduledTaskRequest $request) | ||||
|     public function store(StoreSchedulerRequest $request) | ||||
|     { | ||||
|         $scheduler = new Scheduler(); | ||||
|         $scheduler->service()->store($scheduler, $request); | ||||
|         $scheduler = $this->scheduler_repository->save($request->all(), SchedulerFactory::create(auth()->user()->company()->id, auth()->user()->id)); | ||||
| 
 | ||||
|         return $this->itemResponse($scheduler); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @OA\GET( | ||||
|      *      path="/api/v1/task_scheduler/{id}", | ||||
|      *      path="/api/v1/task_schedulers/{id}", | ||||
|      *      operationId="showTaskScheduler", | ||||
|      *      tags={"task_scheduler"}, | ||||
|      *      tags={"task_schedulers"}, | ||||
|      *      summary="Show given scheduler", | ||||
|      *      description="Get scheduler with associated job", | ||||
|      *      @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), | ||||
| @ -142,16 +190,16 @@ class TaskSchedulerController extends BaseController | ||||
|      *       ), | ||||
|      *     ) | ||||
|      */ | ||||
|     public function show(Scheduler $scheduler) | ||||
|     public function show(ShowSchedulerRequest $request, Scheduler $scheduler) | ||||
|     { | ||||
|         return $this->itemResponse($scheduler); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @OA\PUT( | ||||
|      *      path="/api/v1/task_scheduler/{id}", | ||||
|      *      path="/api/v1/task_schedulers/{id}", | ||||
|      *      operationId="updateTaskScheduler", | ||||
|      *      tags={"task_scheduler"}, | ||||
|      *      tags={"task_schedulers"}, | ||||
|      *      summary="Update task scheduler ", | ||||
|      *      description="Update task scheduler", | ||||
|      * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), | ||||
| @ -168,7 +216,7 @@ class TaskSchedulerController extends BaseController | ||||
|      *          ), | ||||
|      *      ),     * @OA\RequestBody( | ||||
|      *          required=true, | ||||
|      *          @OA\JsonContent(ref="#/components/schemas/UpdateTaskSchedulerSchema") | ||||
|      *          @OA\JsonContent(ref="#/components/schemas/TaskSchedulerSchema") | ||||
|      *      ), | ||||
|      * @OA\Response( | ||||
|      *          response=200, | ||||
| @ -189,18 +237,18 @@ class TaskSchedulerController extends BaseController | ||||
|      *       ), | ||||
|      *     ) | ||||
|      */ | ||||
|     public function update(Scheduler $scheduler, UpdateScheduleRequest $request) | ||||
|     public function update(UpdateSchedulerRequest $request, Scheduler $scheduler) | ||||
|     { | ||||
|         $scheduler->service()->update($scheduler, $request); | ||||
|         $this->scheduler_repository->save($request->all(), $scheduler); | ||||
| 
 | ||||
|         return $this->itemResponse($scheduler); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @OA\DELETE( | ||||
|      *      path="/api/v1/task_scheduler/{id}", | ||||
|      *      path="/api/v1/task_schedulers/{id}", | ||||
|      *      operationId="destroyTaskScheduler", | ||||
|      *      tags={"task_scheduler"}, | ||||
|      *      tags={"task_schedulers"}, | ||||
|      *      summary="Destroy Task Scheduler", | ||||
|      *      description="Destroy task scheduler and its associated job", | ||||
|      *      @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), | ||||
| @ -230,10 +278,83 @@ class TaskSchedulerController extends BaseController | ||||
|      *       ), | ||||
|      *     ) | ||||
|      */ | ||||
|     public function destroy(Scheduler $scheduler) | ||||
|     public function destroy(DestroySchedulerRequest $request, Scheduler $scheduler) | ||||
|     { | ||||
|         $this->scheduler_repository->delete($scheduler); | ||||
| 
 | ||||
|         return $this->itemResponse($scheduler->fresh()); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Perform bulk actions on the list view. | ||||
|      * | ||||
|      * @return Response | ||||
|      * | ||||
|      * | ||||
|      * @OA\Post( | ||||
|      *      path="/api/v1/task_schedulers/bulk", | ||||
|      *      operationId="bulkTaskSchedulerActions", | ||||
|      *      tags={"task_schedulers"}, | ||||
|      *      summary="Performs bulk actions on an array of task_schedulers", | ||||
|      *      description="", | ||||
|      *      @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), | ||||
|      *      @OA\Parameter(ref="#/components/parameters/X-Api-Token"), | ||||
|      *      @OA\Parameter(ref="#/components/parameters/X-Requested-With"), | ||||
|      *      @OA\Parameter(ref="#/components/parameters/index"), | ||||
|      *      @OA\RequestBody( | ||||
|      *         description="array of ids", | ||||
|      *         required=true, | ||||
|      *         @OA\MediaType( | ||||
|      *             mediaType="application/json", | ||||
|      *             @OA\Schema( | ||||
|      *                 type="array", | ||||
|      *                 @OA\Items( | ||||
|      *                     type="integer", | ||||
|      *                     description="Array of hashed IDs to be bulk 'actioned", | ||||
|      *                     example="[0,1,2,3]", | ||||
|      *                 ), | ||||
|      *             ) | ||||
|      *         ) | ||||
|      *     ), | ||||
|      *      @OA\Response( | ||||
|      *          response=200, | ||||
|      *          description="The TaskSchedule response", | ||||
|      *          @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), | ||||
|      *          @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), | ||||
|      *          @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), | ||||
|      *          @OA\JsonContent(ref="#/components/schemas/TaskScheduleSchema"), | ||||
|      *       ), | ||||
|      *       @OA\Response( | ||||
|      *          response=422, | ||||
|      *          description="Validation error", | ||||
|      *          @OA\JsonContent(ref="#/components/schemas/ValidationError"), | ||||
|      *       ), | ||||
|      *       @OA\Response( | ||||
|      *           response="default", | ||||
|      *           description="Unexpected Error", | ||||
|      *           @OA\JsonContent(ref="#/components/schemas/Error"), | ||||
|      *       ), | ||||
|      *     ) | ||||
|      */ | ||||
|     public function bulk() | ||||
|     { | ||||
|         $action = request()->input('action'); | ||||
| 
 | ||||
|         if(!in_array($action, ['archive', 'restore', 'delete'])) | ||||
|             return response()->json(['message' => 'Bulk action does not exist'], 400); | ||||
| 
 | ||||
|         $ids = request()->input('ids'); | ||||
| 
 | ||||
|         $task_schedulers = Scheduler::withTrashed()->find($this->transformKeys($ids)); | ||||
| 
 | ||||
|         $task_schedulers->each(function ($task_scheduler, $key) use ($action) { | ||||
|             if (auth()->user()->can('edit', $task_scheduler)) { | ||||
|                 $this->scheduler_repository->{$action}($task_scheduler); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         return $this->listResponse(Scheduler::withTrashed()->whereIn('id', $this->transformKeys($ids))); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -174,6 +174,8 @@ class BillingPortalPurchase extends Component | ||||
|      */ | ||||
|     public $company; | ||||
| 
 | ||||
|     public $db; | ||||
| 
 | ||||
|     /** | ||||
|      * Campaign reference. | ||||
|      * | ||||
| @ -183,7 +185,11 @@ class BillingPortalPurchase extends Component | ||||
| 
 | ||||
|     public function mount() | ||||
|     { | ||||
|         MultiDB::setDb($this->company->db); | ||||
|         MultiDB::setDb($this->db); | ||||
| 
 | ||||
|         $this->subscription = Subscription::with('company')->find($this->subscription); | ||||
| 
 | ||||
|         $this->company = $this->subscription->company; | ||||
| 
 | ||||
|         $this->quantity = 1; | ||||
| 
 | ||||
| @ -196,6 +202,12 @@ class BillingPortalPurchase extends Component | ||||
|         elseif(strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0){ | ||||
|             $this->price = $this->subscription->promo_price; | ||||
|         } | ||||
| 
 | ||||
|         /* Leave this here, otherwise a logged in user will need to reauth... painfully */ | ||||
|         if(Auth::guard('contact')->check()){ | ||||
|             return $this->getPaymentMethods(auth()->guard('contact')->user()); | ||||
|         } | ||||
|          | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -225,10 +237,10 @@ class BillingPortalPurchase extends Component | ||||
| 
 | ||||
|         $this->steps['existing_user'] = false; | ||||
| 
 | ||||
|         $contact = $this->createBlankClient(); | ||||
|         $this->contact = $this->createBlankClient(); | ||||
| 
 | ||||
|         if ($contact && $contact instanceof ClientContact) { | ||||
|             $this->getPaymentMethods($contact); | ||||
|         if ($this->contact && $this->contact instanceof ClientContact) { | ||||
|             $this->getPaymentMethods($this->contact); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -265,9 +277,6 @@ class BillingPortalPurchase extends Component | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| // nlog($this->subscription->group_settings->settings);
 | ||||
| // nlog($this->subscription->group_settings->settings->currency_id);
 | ||||
| 
 | ||||
|         if(array_key_exists('currency_id', $this->request_data)) { | ||||
| 
 | ||||
|             $currency = Cache::get('currencies')->filter(function ($item){ | ||||
|  | ||||
| @ -15,6 +15,7 @@ use App\DataMapper\ClientSettings; | ||||
| use App\Factory\ClientFactory; | ||||
| use App\Jobs\Mail\NinjaMailerJob; | ||||
| use App\Jobs\Mail\NinjaMailerObject; | ||||
| use App\Jobs\Subscription\CleanStaleInvoiceOrder; | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Mail\ContactPasswordlessLogin; | ||||
| use App\Mail\Subscription\OtpCode; | ||||
| @ -120,7 +121,7 @@ class BillingPortalPurchasev2 extends Component | ||||
|      * | ||||
|      * @var array | ||||
|      */ | ||||
|     public $request_data; | ||||
|     public $request_data = []; | ||||
| 
 | ||||
|     /** | ||||
|      * Instance of company. | ||||
| @ -129,6 +130,14 @@ class BillingPortalPurchasev2 extends Component | ||||
|      */ | ||||
|     public $company; | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Instance of company. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     public string $db; | ||||
| 
 | ||||
|     /** | ||||
|      * Campaign reference. | ||||
|      * | ||||
| @ -151,10 +160,23 @@ class BillingPortalPurchasev2 extends Component | ||||
|     public $valid_coupon = false; | ||||
|     public $payable_invoices = []; | ||||
|     public $payment_confirmed = false; | ||||
|     public $is_eligible = true; | ||||
|     public $not_eligible_message = ''; | ||||
|      | ||||
|     public function mount() | ||||
|     { | ||||
|         MultiDB::setDb($this->company->db); | ||||
|         MultiDB::setDb($this->db); | ||||
| 
 | ||||
|         $this->subscription = Subscription::with('company')->find($this->subscription); | ||||
| 
 | ||||
|         $this->company = $this->subscription->company; | ||||
| 
 | ||||
|         if(auth()->guard('contact')->check()){ | ||||
|             $this->email = auth()->guard('contact')->user()->email; | ||||
|             $this->contact = auth()->guard('contact')->user(); | ||||
|             $this->authenticated = true; | ||||
|             $this->payment_started = true; | ||||
|         } | ||||
| 
 | ||||
|         $this->discount = 0; | ||||
|         $this->sub_total = 0; | ||||
| @ -177,7 +199,7 @@ class BillingPortalPurchasev2 extends Component | ||||
|             $this->coupon = request()->query('coupon'); | ||||
|             $this->handleCoupon(); | ||||
|         } | ||||
|         elseif(strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0){ | ||||
|         elseif(isset($this->subscription->promo_code) && strlen($this->subscription->promo_code) == 0 && $this->subscription->promo_discount > 0){ | ||||
|             $this->price = $this->subscription->promo_price;  | ||||
|         } | ||||
|          | ||||
| @ -224,6 +246,8 @@ class BillingPortalPurchasev2 extends Component | ||||
| 
 | ||||
|     public function resetEmail() | ||||
|     { | ||||
|         $this->resetErrorBag('login'); | ||||
|         $this->resetValidation('login');   | ||||
|         $this->email = null; | ||||
|     } | ||||
| 
 | ||||
| @ -402,7 +426,6 @@ class BillingPortalPurchasev2 extends Component | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
| @ -488,9 +511,20 @@ class BillingPortalPurchasev2 extends Component | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function handleBeforePaymentEvents() :void | ||||
|     public function handleBeforePaymentEvents() :self | ||||
|     { | ||||
| 
 | ||||
|         $eligibility_check = $this->subscription->service()->isEligible($this->contact); | ||||
| 
 | ||||
|         if(is_array($eligibility_check) && $eligibility_check['message'] != 'Success'){ | ||||
|              | ||||
|             $this->is_eligible = false; | ||||
|             $this->not_eligible_message = $eligibility_check['message']; | ||||
| 
 | ||||
|             return $this; | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         $data = [ | ||||
|             'client_id' => $this->contact->client->id, | ||||
|             'date' => now()->format('Y-m-d'), | ||||
| @ -500,19 +534,9 @@ class BillingPortalPurchasev2 extends Component | ||||
|             ]], | ||||
|             'user_input_promo_code' => $this->coupon, | ||||
|             'coupon' => empty($this->subscription->promo_code) ? '' : $this->coupon, | ||||
|             // 'quantity' => $this->quantity,
 | ||||
| 
 | ||||
|         ]; | ||||
| 
 | ||||
|         $is_eligible = $this->subscription->service()->isEligible($this->contact); | ||||
| 
 | ||||
|         // if (is_array($is_eligible) && $is_eligible['message'] != 'Success') {
 | ||||
|         //     $this->steps['not_eligible'] = true;
 | ||||
|         //     $this->steps['not_eligible_message'] = $is_eligible['message'];
 | ||||
|         //     $this->steps['show_loading_bar'] = false;
 | ||||
| 
 | ||||
|         //     return;
 | ||||
|         // }
 | ||||
| 
 | ||||
|         $this->invoice = $this->subscription | ||||
|             ->service() | ||||
|             ->createInvoiceV2($this->bundle, $this->contact->client_id, $this->valid_coupon) | ||||
| @ -533,9 +557,21 @@ class BillingPortalPurchasev2 extends Component | ||||
|         ], now()->addMinutes(60)); | ||||
| 
 | ||||
|         $this->emit('beforePaymentEventsCompleted'); | ||||
| 
 | ||||
|         return $this; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public function handleTrial() | ||||
|     { | ||||
|         return $this->subscription->service()->startTrial([ | ||||
|             'email' => $this->email ?? $this->contact->email, | ||||
|             'quantity' => $this->quantity, | ||||
|             'contact_id' => $this->contact->id, | ||||
|             'client_id' => $this->contact->client->id, | ||||
|             'bundle' => $this->bundle, | ||||
|         ]); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -13,6 +13,7 @@ | ||||
| namespace App\Http\Livewire; | ||||
| 
 | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Models\Company; | ||||
| use App\Models\Credit; | ||||
| use App\Utils\Traits\WithSorting; | ||||
| use Livewire\Component; | ||||
| @ -23,26 +24,31 @@ class CreditsTable extends Component | ||||
|     use WithPagination; | ||||
|     use WithSorting; | ||||
| 
 | ||||
|     public $per_page = 10; | ||||
|     public int $per_page = 10; | ||||
| 
 | ||||
|     public $company; | ||||
|     public Company $company; | ||||
| 
 | ||||
|     public string $db; | ||||
| 
 | ||||
|     public int $company_id; | ||||
| 
 | ||||
|     public function mount() | ||||
|     { | ||||
|         MultiDB::setDb($this->company->db); | ||||
|         MultiDB::setDb($this->db); | ||||
| 
 | ||||
|         $this->company = Company::find($this->company_id); | ||||
|     } | ||||
| 
 | ||||
|     public function render() | ||||
|     { | ||||
|         $query = Credit::query() | ||||
|             ->where('client_id', auth()->guard('contact')->user()->client_id) | ||||
|             ->where('company_id', $this->company->id) | ||||
|             ->where('client_id', auth()->guard('contact')->user()->client_id) | ||||
|             ->where('status_id', '<>', Credit::STATUS_DRAFT) | ||||
|             ->where('is_deleted', 0) | ||||
|             ->where(function ($query) { | ||||
|                 $query->whereDate('due_date', '>=', now()) | ||||
|                       ->orWhereNull('due_date'); | ||||
|                 //->orWhere('due_date', '=', '');
 | ||||
|             }) | ||||
|             ->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc') | ||||
|             ->withTrashed() | ||||
|  | ||||
| @ -14,6 +14,7 @@ namespace App\Http\Livewire; | ||||
| 
 | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Models\Client; | ||||
| use App\Models\Company; | ||||
| use App\Models\Credit; | ||||
| use App\Models\Document; | ||||
| use App\Models\Expense; | ||||
| @ -31,21 +32,27 @@ class DocumentsTable extends Component | ||||
| { | ||||
|     use WithPagination, WithSorting; | ||||
| 
 | ||||
|     public $client; | ||||
|     public Company $company; | ||||
| 
 | ||||
|     public $per_page = 10; | ||||
|     public Client $client; | ||||
| 
 | ||||
|     public $company; | ||||
|     public int $client_id; | ||||
| 
 | ||||
|     public int $per_page = 10; | ||||
| 
 | ||||
|     public string $tab = 'documents'; | ||||
| 
 | ||||
|     public string $db; | ||||
| 
 | ||||
|     protected $query; | ||||
| 
 | ||||
|     public function mount($client) | ||||
|     public function mount() | ||||
|     { | ||||
|         MultiDB::setDb($this->company->db); | ||||
|         MultiDB::setDb($this->db); | ||||
| 
 | ||||
|         $this->client = $client; | ||||
|         $this->client = Client::with('company')->find($this->client_id); | ||||
| 
 | ||||
|         $this->company = $this->client->company; | ||||
| 
 | ||||
|         $this->query = $this->documents(); | ||||
|     } | ||||
|  | ||||
| @ -13,6 +13,7 @@ | ||||
| namespace App\Http\Livewire; | ||||
| 
 | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Models\Company; | ||||
| use App\Models\Invoice; | ||||
| use App\Utils\Traits\WithSorting; | ||||
| use Carbon\Carbon; | ||||
| @ -23,15 +24,21 @@ class InvoicesTable extends Component | ||||
| { | ||||
|     use WithPagination, WithSorting; | ||||
| 
 | ||||
|     public $per_page = 10; | ||||
|     public int $per_page = 10; | ||||
| 
 | ||||
|     public $status = []; | ||||
|     public array $status = []; | ||||
| 
 | ||||
|     public $company; | ||||
|     public Company $company; | ||||
| 
 | ||||
|     public int $company_id; | ||||
| 
 | ||||
|     public string $db; | ||||
| 
 | ||||
|     public function mount() | ||||
|     { | ||||
|         MultiDB::setDb($this->company->db); | ||||
|         MultiDB::setDb($this->db); | ||||
| 
 | ||||
|         $this->company = Company::find($this->company_id); | ||||
| 
 | ||||
|         $this->sort_asc = false; | ||||
| 
 | ||||
|  | ||||
| @ -23,13 +23,11 @@ class PayNowDropdown extends Component | ||||
| 
 | ||||
|     public $company; | ||||
| 
 | ||||
|     public function mount(int $total) | ||||
|     public function mount() | ||||
|     { | ||||
|         MultiDB::setDb($this->company->db); | ||||
| 
 | ||||
|         $this->total = $total; | ||||
| 
 | ||||
|         $this->methods = auth()->guard('contact')->user()->client->service()->getPaymentMethods($total); | ||||
|         $this->methods = auth()->guard('contact')->user()->client->service()->getPaymentMethods($this->total); | ||||
|     } | ||||
| 
 | ||||
|     public function render() | ||||
|  | ||||
| @ -3,7 +3,9 @@ | ||||
| namespace App\Http\Livewire; | ||||
| 
 | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Models\Client; | ||||
| use App\Models\ClientGatewayToken; | ||||
| use App\Models\Company; | ||||
| use App\Utils\Traits\WithSorting; | ||||
| use Livewire\Component; | ||||
| use Livewire\WithPagination; | ||||
| @ -13,17 +15,23 @@ class PaymentMethodsTable extends Component | ||||
|     use WithPagination; | ||||
|     use WithSorting; | ||||
| 
 | ||||
|     public $per_page = 10; | ||||
|     public int $per_page = 10; | ||||
| 
 | ||||
|     public $client; | ||||
|     public Client $client; | ||||
| 
 | ||||
|     public $company; | ||||
|     public Company $company; | ||||
| 
 | ||||
|     public function mount($client) | ||||
|     public int $client_id; | ||||
| 
 | ||||
|     public string $db; | ||||
| 
 | ||||
|     public function mount() | ||||
|     { | ||||
|         MultiDB::setDb($this->company->db); | ||||
|         MultiDB::setDb($this->db); | ||||
| 
 | ||||
|         $this->client = $client; | ||||
|         $this->client = Client::with('company')->find($this->client_id); | ||||
| 
 | ||||
|         $this->company = $this->client->company; | ||||
|     } | ||||
| 
 | ||||
|     public function render() | ||||
|  | ||||
| @ -13,6 +13,7 @@ | ||||
| namespace App\Http\Livewire; | ||||
| 
 | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Models\Company; | ||||
| use App\Models\Payment; | ||||
| use App\Utils\Traits\WithSorting; | ||||
| use Livewire\Component; | ||||
| @ -23,17 +24,19 @@ class PaymentsTable extends Component | ||||
|     use WithSorting; | ||||
|     use WithPagination; | ||||
| 
 | ||||
|     public $per_page = 10; | ||||
|     public int $per_page = 10; | ||||
| 
 | ||||
|     public $user; | ||||
|     public Company $company; | ||||
| 
 | ||||
|     public $company; | ||||
|     public int $company_id; | ||||
| 
 | ||||
|     public string $db; | ||||
| 
 | ||||
|     public function mount() | ||||
|     { | ||||
|         MultiDB::setDb($this->company->db); | ||||
|         MultiDB::setDb($this->db); | ||||
| 
 | ||||
|         $this->user = auth()->user(); | ||||
|         $this->company = Company::find($this->company_id); | ||||
|     } | ||||
| 
 | ||||
|     public function render() | ||||
| @ -43,7 +46,7 @@ class PaymentsTable extends Component | ||||
|             ->where('company_id', $this->company->id) | ||||
|             ->where('client_id', auth()->guard('contact')->user()->client_id) | ||||
|             ->whereIn('status_id', [Payment::STATUS_FAILED, Payment::STATUS_COMPLETED, Payment::STATUS_PENDING, Payment::STATUS_REFUNDED, Payment::STATUS_PARTIALLY_REFUNDED]) | ||||
|             ->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc') | ||||
|             ->orderBy($this->sort_field, $this->sort_asc ? 'desc' : 'asc') | ||||
|             ->withTrashed() | ||||
|             ->paginate($this->per_page); | ||||
| 
 | ||||
|  | ||||
| @ -13,6 +13,7 @@ | ||||
| namespace App\Http\Livewire; | ||||
| 
 | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Models\Company; | ||||
| use App\Models\Quote; | ||||
| use App\Utils\Traits\WithSorting; | ||||
| use Livewire\Component; | ||||
| @ -22,15 +23,27 @@ class QuotesTable extends Component | ||||
| { | ||||
|     use WithPagination; | ||||
| 
 | ||||
|     public $per_page = 10; | ||||
|     public int $per_page = 10; | ||||
| 
 | ||||
|     public $status = []; | ||||
|     public array $status = []; | ||||
| 
 | ||||
|     public $company; | ||||
|     public Company $company; | ||||
| 
 | ||||
|     public $sort = 'status_id'; // Default sortBy. Feel free to change or pull from client/company settings.
 | ||||
|     public string $sort = 'status_id';  | ||||
| 
 | ||||
|     public bool $sort_asc = true; | ||||
| 
 | ||||
|     public int $company_id; | ||||
| 
 | ||||
|     public string $db; | ||||
| 
 | ||||
|     public function mount() | ||||
|     { | ||||
|         MultiDB::setDb($this->db); | ||||
| 
 | ||||
|         $this->company = Company::find($this->company_id); | ||||
|     } | ||||
| 
 | ||||
|     public $sort_asc = true; | ||||
| 
 | ||||
|     public function sortBy($field) | ||||
|     { | ||||
| @ -41,16 +54,11 @@ class QuotesTable extends Component | ||||
|         $this->sort = $field; | ||||
|     } | ||||
| 
 | ||||
|     public function mount() | ||||
|     { | ||||
|         MultiDB::setDb($this->company->db); | ||||
|     } | ||||
| 
 | ||||
|     public function render() | ||||
|     { | ||||
| 
 | ||||
|         $query = Quote::query() | ||||
|             ->with('client.gateway_tokens', 'company', 'client.contacts') | ||||
|             ->with('client.contacts', 'company') | ||||
|             ->orderBy($this->sort, $this->sort_asc ? 'asc' : 'desc'); | ||||
| 
 | ||||
|         if (count($this->status) > 0) { | ||||
|  | ||||
| @ -142,7 +142,7 @@ class SubscriptionPlanSwitch extends Component | ||||
|     { | ||||
|         $this->hide_button = true; | ||||
| 
 | ||||
|         $response =  $this->target->service()->createChangePlanCredit([ | ||||
|         $response =  $this->target->service()->createChangePlanCreditV2([ | ||||
|             'recurring_invoice' => $this->recurring_invoice, | ||||
|             'subscription' => $this->subscription, | ||||
|             'target' => $this->target, | ||||
|  | ||||
							
								
								
									
										51
									
								
								app/Http/Livewire/SubscriptionsTable.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								app/Http/Livewire/SubscriptionsTable.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | ||||
| <?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\Http\Livewire; | ||||
| 
 | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Models\RecurringInvoice; | ||||
| use App\Utils\Traits\WithSorting; | ||||
| use Livewire\Component; | ||||
| use Livewire\WithPagination; | ||||
| 
 | ||||
| class SubscriptionsTable extends Component | ||||
| { | ||||
|     use WithPagination; | ||||
|     use WithSorting; | ||||
| 
 | ||||
|     public $per_page = 10; | ||||
| 
 | ||||
|     public $company; | ||||
| 
 | ||||
|     public function mount() | ||||
|     { | ||||
|         MultiDB::setDb($this->company->db); | ||||
|     } | ||||
| 
 | ||||
|     public function render() | ||||
|     { | ||||
|         $query = RecurringInvoice::query() | ||||
|             ->where('client_id', auth()->guard('contact')->user()->client->id) | ||||
|             ->where('company_id', $this->company->id) | ||||
|             ->whereNotNull('subscription_id') | ||||
|             ->where('is_deleted', false) | ||||
|             ->where('status_id', RecurringInvoice::STATUS_ACTIVE) | ||||
|             ->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc') | ||||
|             ->withTrashed() | ||||
|             ->paginate($this->per_page); | ||||
| 
 | ||||
|         return render('components.livewire.subscriptions-table', [ | ||||
|             'recurring_invoices' => $query, | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @ -18,6 +18,7 @@ use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
| use Illuminate\Support\Facades\Hash; | ||||
| use Illuminate\Support\Str; | ||||
| use Laravel\Socialite\Facades\Socialite; | ||||
| use stdClass; | ||||
| 
 | ||||
| class PasswordProtection | ||||
| @ -111,7 +112,18 @@ class PasswordProtection | ||||
|                     return $next($request); | ||||
|                 } | ||||
|             } | ||||
|             elseif(auth()->user()->oauth_provider_id == 'apple') | ||||
|             { | ||||
|                  | ||||
|                 $user = Socialite::driver('apple')->userFromToken($request->header('X-API-OAUTH-PASSWORD')); | ||||
| 
 | ||||
|                 if($user && ($user->email == auth()->user()->email)){ | ||||
| 
 | ||||
|                     Cache::put(auth()->user()->hashed_id.'_'.auth()->user()->account_id.'_logged_in', Str::random(64), $timeout); | ||||
|                     return $next($request); | ||||
|                 } | ||||
|                  | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|             return response()->json($error, 412); | ||||
|  | ||||
| @ -64,21 +64,22 @@ class MatchBankTransactionRequest extends Request | ||||
| 
 | ||||
|             if(array_key_exists('payment_id', $inputs['transactions'][$key]) && strlen($inputs['transactions'][$key]['payment_id']) >= 1){ | ||||
|                 $inputs['transactions'][$key]['payment_id'] = $this->decodePrimaryKey($inputs['transactions'][$key]['payment_id']); | ||||
|                 $p = Payment::withTrashed()->find($inputs['transactions'][$key]['payment_id']); | ||||
|                 $p = Payment::withTrashed()->where('company_id', auth()->user()->company()->id)->where('id', $inputs['transactions'][$key]['payment_id'])->first(); | ||||
| 
 | ||||
|                 /*Ensure we don't relink an existing payment*/ | ||||
|                 if(!$p || $p->transaction_id) | ||||
|                     $inputs['transactions'][$key]['payment_id'] = null; | ||||
|                 if(!$p || is_numeric($p->transaction_id)){ | ||||
|                     unset($inputs['transactions'][$key]); | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
| 
 | ||||
|             if(array_key_exists('expense_id', $inputs['transactions'][$key]) && strlen($inputs['transactions'][$key]['expense_id']) >= 1){ | ||||
|                 $inputs['transactions'][$key]['expense_id'] = $this->decodePrimaryKey($inputs['transactions'][$key]['expense_id']); | ||||
|                 $e = Expense::withTrashed()->find($inputs['transactions'][$key]['expense_id']); | ||||
|                 $e = Expense::withTrashed()->where('company_id', auth()->user()->company()->id)->where('id', $inputs['transactions'][$key]['expense_id'])->first(); | ||||
| 
 | ||||
|                 /*Ensure we don't relink an existing expense*/ | ||||
|                 if(!$e || $e->transaction_id) | ||||
|                     $inputs['transactions'][$key]['expense_id'] = null; | ||||
|                 if(!$e || is_numeric($e->transaction_id)) | ||||
|                     unset($inputs['transactions'][$key]['expense_id']); | ||||
| 
 | ||||
|             } | ||||
| 
 | ||||
|  | ||||
| @ -47,7 +47,7 @@ class RegisterRequest extends FormRequest | ||||
| 
 | ||||
|         foreach ($rules as $field => $properties) { | ||||
|             if ($field === 'email') { | ||||
|                 $rules[$field] = array_merge($rules[$field], ['email:rfc,dns', 'max:255', Rule::unique('client_contacts')->where('company_id', $this->company()->id)]); | ||||
|                 $rules[$field] = array_merge($rules[$field], ['email:rfc,dns', 'max:191', Rule::unique('client_contacts')->where('company_id', $this->company()->id)]); | ||||
|             } | ||||
| 
 | ||||
|             if ($field === 'current_password') { | ||||
|  | ||||
| @ -40,8 +40,6 @@ class UpdateCompanyRequest extends Request | ||||
|         return auth()->user()->can('edit', $this->company); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     public function rules() | ||||
|     { | ||||
|         $input = $this->all(); | ||||
| @ -110,7 +108,8 @@ class UpdateCompanyRequest extends Request | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         $settings['email_style_custom'] = str_replace(['{{','}}'], ['',''], $settings['email_style_custom']); | ||||
|         if(isset($settings['email_style_custom'])) | ||||
|             $settings['email_style_custom'] = str_replace(['{{','}}'], ['',''], $settings['email_style_custom']); | ||||
| 
 | ||||
|         if (! $account->isFreeHostedClient()) { | ||||
|             return $settings; | ||||
|  | ||||
| @ -41,7 +41,7 @@ class StoreExpenseRequest extends Request | ||||
|             $rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id); | ||||
|         } | ||||
| 
 | ||||
|         if (! empty($this->client_id)) { | ||||
|         if ($this->client_id) { | ||||
|             $rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id; | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -41,6 +41,10 @@ class UpdateExpenseRequest extends Request | ||||
|             $rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id)->ignore($this->expense->id); | ||||
|         } | ||||
| 
 | ||||
|         if ($this->client_id) { | ||||
|             $rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id; | ||||
|         } | ||||
| 
 | ||||
|         $rules['category_id'] = 'bail|sometimes|nullable|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; | ||||
| 
 | ||||
|         return $this->globalRules($rules); | ||||
|  | ||||
| @ -47,7 +47,8 @@ class PaymentWebhookRequest extends Request | ||||
|     { | ||||
|         MultiDB::findAndSetDbByCompanyKey($this->company_key); | ||||
| 
 | ||||
|         return CompanyGateway::withTrashed()->findOrFail($this->decodePrimaryKey($this->company_gateway_id)); | ||||
|         return CompanyGateway::withTrashed()->find($this->decodePrimaryKey($this->company_gateway_id)); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -43,6 +43,7 @@ class StoreRecurringExpenseRequest extends Request | ||||
|             $rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id; | ||||
|         } | ||||
| 
 | ||||
|         $rules['category_id'] = 'bail|nullable|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; | ||||
|         $rules['frequency_id'] = 'required|integer|digits_between:1,12'; | ||||
|         $rules['tax_amount1'] = 'numeric'; | ||||
|         $rules['tax_amount2'] = 'numeric'; | ||||
| @ -61,10 +62,6 @@ class StoreRecurringExpenseRequest extends Request | ||||
|             $input['next_send_date_client'] = $input['next_send_date']; | ||||
|         } | ||||
| 
 | ||||
|         if (array_key_exists('category_id', $input) && is_string($input['category_id'])) { | ||||
|             $input['category_id'] = $this->decodePrimaryKey($input['category_id']); | ||||
|         } | ||||
| 
 | ||||
|         if (! array_key_exists('currency_id', $input) || strlen($input['currency_id']) == 0) { | ||||
|             $input['currency_id'] = (string) auth()->user()->company()->settings->currency_id; | ||||
|         } | ||||
|  | ||||
| @ -46,6 +46,7 @@ class UpdateRecurringExpenseRequest extends Request | ||||
|         $rules['tax_amount1'] = 'numeric'; | ||||
|         $rules['tax_amount2'] = 'numeric'; | ||||
|         $rules['tax_amount3'] = 'numeric'; | ||||
|         $rules['category_id'] = 'bail|nullable|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; | ||||
| 
 | ||||
|         return $this->globalRules($rules); | ||||
|     } | ||||
| @ -70,10 +71,6 @@ class UpdateRecurringExpenseRequest extends Request | ||||
|             $input['next_send_date_client'] = $input['next_send_date']; | ||||
|         } | ||||
| 
 | ||||
|         if (array_key_exists('category_id', $input) && is_string($input['category_id'])) { | ||||
|             $input['category_id'] = $this->decodePrimaryKey($input['category_id']); | ||||
|         } | ||||
| 
 | ||||
|         if (array_key_exists('documents', $input)) { | ||||
|             unset($input['documents']); | ||||
|         } | ||||
|  | ||||
							
								
								
									
										71
									
								
								app/Http/Requests/Report/ProductSalesReportRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								app/Http/Requests/Report/ProductSalesReportRequest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| <?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\Http\Requests\Report; | ||||
| 
 | ||||
| use App\Http\Requests\Request; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use Illuminate\Validation\Rule; | ||||
| 
 | ||||
| class ProductSalesReportRequest extends Request | ||||
| { | ||||
|     use MakesHash; | ||||
| 
 | ||||
|     /** | ||||
|      * Determine if the user is authorized to make this request. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function authorize() : bool | ||||
|     { | ||||
|         return auth()->user()->isAdmin(); | ||||
|     } | ||||
| 
 | ||||
|     public function rules() | ||||
|     { | ||||
| 
 | ||||
|         return [ | ||||
|             'date_range' => 'bail|required|string', | ||||
|             'end_date' => 'bail|required_if:date_range,custom|nullable|date', | ||||
|             'start_date' => 'bail|required_if:date_range,custom|nullable|date', | ||||
|             'report_keys' => 'bail|present|array', | ||||
|             'send_email' => 'bail|required|bool', | ||||
|             'client_id' => 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id.',is_deleted,0', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     public function prepareForValidation() | ||||
|     { | ||||
|         $input = $this->all(); | ||||
| 
 | ||||
|         if (! array_key_exists('date_range', $input)) { | ||||
|             $input['date_range'] = 'all'; | ||||
|         } | ||||
| 
 | ||||
|         if (! array_key_exists('report_keys', $input)) { | ||||
|             $input['report_keys'] = []; | ||||
|         } | ||||
| 
 | ||||
|         if (! array_key_exists('send_email', $input)) { | ||||
|             $input['send_email'] = true; | ||||
|         } | ||||
| 
 | ||||
|         if (array_key_exists('date_range', $input) && $input['date_range'] != 'custom') { | ||||
|             $input['start_date'] = null; | ||||
|             $input['end_date'] = null; | ||||
|         } | ||||
| 
 | ||||
|         if(array_key_exists('client_id', $input) && strlen($input['client_id']) >=1) | ||||
|             $input['client_id'] = $this->decodePrimaryKey($input['client_id']); | ||||
| 
 | ||||
|         $this->replace($input); | ||||
|     } | ||||
| } | ||||
| @ -32,8 +32,6 @@ class CheckMailRequest extends Request | ||||
|      */ | ||||
|     public function rules() | ||||
|     { | ||||
|         nlog($this->driver); | ||||
| 
 | ||||
|         return [ | ||||
|             'mail_driver' => 'required', | ||||
|             'encryption' => 'required_unless:mail_driver,log', | ||||
|  | ||||
| @ -1,39 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App\Http\Requests\TaskScheduler; | ||||
| 
 | ||||
| use App\Http\Requests\Request; | ||||
| 
 | ||||
| class CreateScheduledTaskRequest extends Request | ||||
| { | ||||
|     /** | ||||
|      * Determine if the user is authorized to make this request. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function authorize(): bool | ||||
|     { | ||||
|         return auth()->user()->isAdmin(); | ||||
|     } | ||||
| 
 | ||||
|     public function rules() | ||||
|     { | ||||
|         return [ | ||||
|             'paused' => 'sometimes|bool', | ||||
|             'repeat_every' => 'required|string|in:DAY,WEEK,MONTH,3MONTHS,YEAR', | ||||
|             'start_from' => 'sometimes|string', | ||||
|             'job' => 'required', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     public function prepareForValidation() | ||||
|     { | ||||
|         $input = $this->all(); | ||||
| 
 | ||||
|         if (! array_key_exists('start_from', $input)) { | ||||
|             $input['start_from'] = now(); | ||||
|         } | ||||
| 
 | ||||
|         $this->replace($input); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										28
									
								
								app/Http/Requests/TaskScheduler/CreateSchedulerRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/Http/Requests/TaskScheduler/CreateSchedulerRequest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| <?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\Http\Requests\TaskScheduler; | ||||
| 
 | ||||
| use App\Http\Requests\Request; | ||||
| 
 | ||||
| class CreateSchedulerRequest extends Request | ||||
| { | ||||
|     /** | ||||
|      * Determine if the user is authorized to make this request. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function authorize(): bool | ||||
|     { | ||||
|         return auth()->user()->isAdmin(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										27
									
								
								app/Http/Requests/TaskScheduler/DestroySchedulerRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								app/Http/Requests/TaskScheduler/DestroySchedulerRequest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| <?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\Http\Requests\Task; | ||||
| 
 | ||||
| use App\Http\Requests\Request; | ||||
| 
 | ||||
| class DestroySchedulerRequest extends Request | ||||
| { | ||||
|     /** | ||||
|      * Determine if the user is authorized to make this request. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function authorize() : bool | ||||
|     { | ||||
|         return auth()->user()->isAdmin(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										27
									
								
								app/Http/Requests/TaskScheduler/ShowSchedulerRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								app/Http/Requests/TaskScheduler/ShowSchedulerRequest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | ||||
| <?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\Http\Requests\TaskScheduler; | ||||
| 
 | ||||
| use App\Http\Requests\Request; | ||||
| 
 | ||||
| class ShowSchedulerRequest extends Request | ||||
| { | ||||
|     /** | ||||
|      * Determine if the user is authorized to make this request. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function authorize() : bool | ||||
|     { | ||||
|         return auth()->user()->can('view', $this->scheduler); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										44
									
								
								app/Http/Requests/TaskScheduler/StoreSchedulerRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								app/Http/Requests/TaskScheduler/StoreSchedulerRequest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | ||||
| <?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\Http\Requests\TaskScheduler; | ||||
| 
 | ||||
| use App\Http\Requests\Request; | ||||
| use Illuminate\Validation\Rule; | ||||
| 
 | ||||
| class StoreSchedulerRequest extends Request | ||||
| { | ||||
|     /** | ||||
|      * Determine if the user is authorized to make this request. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function authorize(): bool | ||||
|     { | ||||
|         return auth()->user()->isAdmin(); | ||||
|     } | ||||
| 
 | ||||
|     public function rules() | ||||
|     { | ||||
| 
 | ||||
|         $rules = [ | ||||
|             'name' => ['bail', 'required', Rule::unique('schedulers')->where('company_id', auth()->user()->company()->id)], | ||||
|             'is_paused' => 'bail|sometimes|boolean', | ||||
|             'frequency_id' => 'bail|required|integer|digits_between:1,12', | ||||
|             'next_run' => 'bail|required|date:Y-m-d', | ||||
|             'template' => 'bail|required|string', | ||||
|             'parameters' => 'bail|array', | ||||
|         ]; | ||||
| 
 | ||||
|         return $rules; | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| @ -1,25 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App\Http\Requests\TaskScheduler; | ||||
| 
 | ||||
| use App\Http\Requests\Request; | ||||
| 
 | ||||
| class UpdateScheduledJobRequest extends Request | ||||
| { | ||||
|     /** | ||||
|      * Determine if the user is authorized to make this request. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function authorize(): bool | ||||
|     { | ||||
|         return auth()->user()->isAdmin(); | ||||
|     } | ||||
| 
 | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             'action_name' => 'sometimes|string', | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
| @ -8,14 +8,12 @@ | ||||
|  * | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Http\Requests\TaskScheduler; | ||||
| 
 | ||||
| use App\Http\Requests\Request; | ||||
| use Carbon\Carbon; | ||||
| use Illuminate\Validation\Rule; | ||||
| 
 | ||||
| class UpdateScheduleRequest extends Request | ||||
| class UpdateSchedulerRequest extends Request | ||||
| { | ||||
|     /** | ||||
|      * Determine if the user is authorized to make this request. | ||||
| @ -29,23 +27,17 @@ class UpdateScheduleRequest extends Request | ||||
| 
 | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             'paused' => 'sometimes|bool', | ||||
|             'repeat_every' => 'sometimes|string|in:DAY,WEEK,BIWEEKLY,MONTH,3MONTHS,YEAR', | ||||
|             'start_from' => 'sometimes', | ||||
|             'scheduled_run'=>'sometimes', | ||||
| 
 | ||||
|         $rules = [ | ||||
|             'name' => ['bail', 'sometimes', Rule::unique('schedulers')->where('company_id', auth()->user()->company()->id)->ignore($this->task_scheduler->id)], | ||||
|             'is_paused' => 'bail|sometimes|boolean', | ||||
|             'frequency_id' => 'bail|required|integer|digits_between:1,12', | ||||
|             'next_run' => 'bail|required|date:Y-m-d', | ||||
|             'template' => 'bail|required|string', | ||||
|             'parameters' => 'bail|array', | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     public function prepareForValidation() | ||||
|     { | ||||
|         $input = $this->all(); | ||||
|         return $rules; | ||||
|          | ||||
|         if (isset($input['start_from'])) { | ||||
|             $input['scheduled_run'] = Carbon::parse((int) $input['start_from']); | ||||
|             $input['start_from'] = Carbon::parse((int) $input['start_from']); | ||||
|         } | ||||
| 
 | ||||
|         $this->replace($input); | ||||
|     } | ||||
| } | ||||
| @ -29,6 +29,7 @@ class BlackListRule implements Rule | ||||
|         'dataservices.space', | ||||
|         'karenkey.com', | ||||
|         'sharklasers.com', | ||||
|         '100072641.help' | ||||
|     ]; | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -20,7 +20,7 @@ use App\Http\Requests\Quote\StoreQuoteRequest; | ||||
| use App\Import\ImportException; | ||||
| use App\Jobs\Mail\NinjaMailerJob; | ||||
| use App\Jobs\Mail\NinjaMailerObject; | ||||
| use App\Mail\Import\ImportCompleted; | ||||
| use App\Mail\Import\CsvImportCompleted; | ||||
| use App\Models\Company; | ||||
| use App\Models\Invoice; | ||||
| use App\Models\Quote; | ||||
| @ -187,6 +187,10 @@ class BaseImport | ||||
| 
 | ||||
|             try { | ||||
|                 $entity = $this->transformer->transform($record); | ||||
| 
 | ||||
|                 if(!$entity) | ||||
|                     continue; | ||||
| 
 | ||||
|                 $validator = $this->runValidation($entity); | ||||
| 
 | ||||
|                 if ($validator->fails()) { | ||||
| @ -282,6 +286,8 @@ class BaseImport | ||||
| 
 | ||||
|     public function ingestInvoices($invoices, $invoice_number_key) | ||||
|     { | ||||
|         $count = 0; | ||||
| 
 | ||||
|         $invoice_transformer = $this->transformer; | ||||
| 
 | ||||
|         /** @var PaymentRepository $payment_repository */ | ||||
| @ -343,6 +349,7 @@ class BaseImport | ||||
|                     } | ||||
|                     $invoice_repository->save($invoice_data, $invoice); | ||||
| 
 | ||||
|                     $count++; | ||||
|                     // If we're doing a generic CSV import, only import payment data if we're not importing a payment CSV.
 | ||||
|                     // If we're doing a platform-specific import, trust the platform to only return payment info if there's not a separate payment CSV.
 | ||||
|                     if ( | ||||
| @ -404,6 +411,9 @@ class BaseImport | ||||
|                 ]; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $count; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private function actionInvoiceStatus( | ||||
| @ -475,6 +485,8 @@ class BaseImport | ||||
| 
 | ||||
|     public function ingestQuotes($quotes, $quote_number_key) | ||||
|     { | ||||
|         $count = 0; | ||||
| 
 | ||||
|         $quote_transformer = $this->transformer; | ||||
| 
 | ||||
|         /** @var ClientRepository $client_repository */ | ||||
| @ -533,6 +545,8 @@ class BaseImport | ||||
|                     } | ||||
|                     $quote_repository->save($quote_data, $quote); | ||||
|                      | ||||
|                     $count++; | ||||
| 
 | ||||
|                     $this->actionQuoteStatus( | ||||
|                         $quote, | ||||
|                         $quote_data, | ||||
| @ -552,7 +566,11 @@ class BaseImport | ||||
|                     'error' => $message, | ||||
|                 ]; | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         return $count; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     protected function getUserIDForRecord($record) | ||||
| @ -586,10 +604,11 @@ class BaseImport | ||||
|         $data = [ | ||||
|             'errors'  => $this->error_array, | ||||
|             'company' => $this->company, | ||||
|             'entity_count' => $this->entity_count | ||||
|         ]; | ||||
| 
 | ||||
|         $nmo = new NinjaMailerObject; | ||||
|         $nmo->mailable = new ImportCompleted($this->company, $data); | ||||
|         $nmo->mailable = new CsvImportCompleted($this->company, $data); | ||||
|         $nmo->company = $this->company; | ||||
|         $nmo->settings = $this->company->settings; | ||||
|         $nmo->to_user = $this->company->owner(); | ||||
|  | ||||
| @ -37,7 +37,7 @@ use App\Import\Transformer\Csv\PaymentTransformer; | ||||
| use App\Import\Transformer\Csv\ProductTransformer; | ||||
| use App\Import\Transformer\Csv\QuoteTransformer; | ||||
| use App\Import\Transformer\Csv\VendorTransformer; | ||||
| use App\Import\Transformers\Bank\BankTransformer; | ||||
| use App\Import\Transformer\Bank\BankTransformer; | ||||
| use App\Repositories\BankTransactionRepository; | ||||
| use App\Repositories\ClientRepository; | ||||
| use App\Repositories\ExpenseRepository; | ||||
|  | ||||
| @ -66,7 +66,6 @@ class Wave extends BaseImport implements ImportInterface | ||||
| 
 | ||||
|         if (empty($data)) { | ||||
|             $this->entity_count['clients'] = 0; | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
| @ -170,11 +169,16 @@ class Wave extends BaseImport implements ImportInterface | ||||
|         $entity_type = 'expense'; | ||||
| 
 | ||||
|         $data = $this->getCsvData($entity_type); | ||||
| 
 | ||||
|         if(!$data){ | ||||
|             $this->entity_count['expense'] = 0; | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         $data = $this->preTransform($data, $entity_type); | ||||
| 
 | ||||
|         if (empty($data)) { | ||||
|             $this->entity_count['expense'] = 0; | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
| @ -212,6 +216,8 @@ class Wave extends BaseImport implements ImportInterface | ||||
| 
 | ||||
|     public function ingestExpenses($data) | ||||
|     { | ||||
|         $count = 0; | ||||
| 
 | ||||
|         $key = 'Transaction ID'; | ||||
| 
 | ||||
|         $expense_transformer = $this->transformer; | ||||
| @ -255,6 +261,7 @@ class Wave extends BaseImport implements ImportInterface | ||||
|                     ); | ||||
| 
 | ||||
|                     $expense_repository->save($expense_data, $expense); | ||||
|                     $count++; | ||||
|                 } | ||||
|             } catch (\Exception $ex) { | ||||
|                 if ($ex instanceof ImportException) { | ||||
| @ -270,5 +277,8 @@ class Wave extends BaseImport implements ImportInterface | ||||
|                 ]; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $count; | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -9,7 +9,7 @@ | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Import\Transformers\Bank; | ||||
| namespace App\Import\Transformer\Bank; | ||||
| 
 | ||||
| use App\Import\ImportException; | ||||
| use App\Import\Transformer\BaseTransformer; | ||||
| @ -61,9 +61,9 @@ class InvoiceTransformer extends BaseTransformer | ||||
|                 'discount'           => $this->getFreshbookQuantityFloat($record, 'Discount Percentage'), | ||||
|                 'is_amount_discount' => false, | ||||
|                 'tax_name1'          => $this->getString($record, 'Tax 1 Type'), | ||||
|                 'tax_rate1'          => $this->getFreshbookQuantityFloat($record, 'Tax 1 Amount'), | ||||
|                 'tax_rate1'          => $this->calcTaxRate($record, 'Tax 1 Amount'), | ||||
|                 'tax_name2'          => $this->getString($record, 'Tax 2 Type'), | ||||
|                 'tax_rate2'          => $this->getFreshbookQuantityFloat($record, 'Tax 2 Amount'), | ||||
|                 'tax_rate2'          => $this->calcTaxRate($record, 'Tax 2 Amount'), | ||||
|             ]; | ||||
|             $transformed['amount'] += $this->getFreshbookQuantityFloat($record, 'Line Total'); | ||||
|         } | ||||
| @ -79,6 +79,27 @@ class InvoiceTransformer extends BaseTransformer | ||||
|         return $transformed; | ||||
|     } | ||||
| 
 | ||||
|     //Line Subtotal
 | ||||
|     public function calcTaxRate($record, $field) | ||||
|     { | ||||
|         if(isset($record['Line Subtotal']) && $record['Line Subtotal'] > 0) | ||||
|             return ($record[$field] / $record['Line Subtotal']) * 100; | ||||
| 
 | ||||
|         $tax_amount1 = isset($record['Tax 1 Amount']) ? $record['Tax 1 Amount'] : 0; | ||||
| 
 | ||||
|         $tax_amount2 = isset($record['Tax 2 Amount']) ? $record['Tax 2 Amount'] : 0; | ||||
| 
 | ||||
|         $line_total = isset($record['Line Total']) ? $record['Line Total'] : 0; | ||||
| 
 | ||||
|         $subtotal = $line_total - $tax_amount2 - $tax_amount1; | ||||
| 
 | ||||
|         if($subtotal > 0) | ||||
|             return $record[$field] / $subtotal * 100; | ||||
| 
 | ||||
|         return 0; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** @return float  */ | ||||
|     public function getFreshbookQuantityFloat($data, $field) | ||||
|     { | ||||
|  | ||||
| @ -28,6 +28,11 @@ class InvoiceTransformer extends BaseTransformer | ||||
|      */ | ||||
|     public function transform($invoice_data) | ||||
|     { | ||||
| 
 | ||||
|         if (!isset($invoice_data['DocumentNumber'])) { | ||||
|             throw new ImportException('DocumentNumber key not found in this import file.'); | ||||
|         } | ||||
| 
 | ||||
|         if ($this->hasInvoice($invoice_data['DocumentNumber'])) { | ||||
|             throw new ImportException('Invoice number already exists'); | ||||
|         } | ||||
|  | ||||
| @ -28,7 +28,8 @@ class ClientTransformer extends BaseTransformer | ||||
|     public function transform($data) | ||||
|     { | ||||
|         if (isset($data['customer_name']) && $this->hasClient($data['customer_name'])) { | ||||
|             throw new ImportException('Client already exists'); | ||||
|             return false; | ||||
|             // throw new ImportException('Client already exists');
 | ||||
|         } | ||||
| 
 | ||||
|         $settings = new \stdClass; | ||||
|  | ||||
| @ -1,430 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Invoice Ninja (https://invoiceninja.com). | ||||
|  * | ||||
|  * @link https://github.com/invoiceninja/invoiceninja source repository | ||||
|  * | ||||
|  * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) | ||||
|  * | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Import\Transformers; | ||||
| 
 | ||||
| use App\Models\ClientContact; | ||||
| use App\Utils\Number; | ||||
| use Carbon; | ||||
| use Exception; | ||||
| 
 | ||||
| /** | ||||
|  * Class BaseTransformer. | ||||
|  */ | ||||
| class BaseTransformer | ||||
| { | ||||
|     /** | ||||
|      * @var | ||||
|      */ | ||||
|     protected $maps; | ||||
| 
 | ||||
|     /** | ||||
|      * BaseTransformer constructor. | ||||
|      * | ||||
|      * @param $maps | ||||
|      */ | ||||
|     public function __construct($maps) | ||||
|     { | ||||
|         $this->maps = $maps; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $data | ||||
|      * @param $field | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getString($data, $field) | ||||
|     { | ||||
|         return (isset($data[$field]) && $data[$field]) ? $data[$field] : ''; | ||||
|     } | ||||
| 
 | ||||
|     public function getInvoiceTypeId($data, $field) | ||||
|     { | ||||
|         return (isset($data[$field]) && $data[$field]) ? $data[$field] : '1'; | ||||
|     } | ||||
| 
 | ||||
|     public function getCurrencyByCode($data, $key = 'client.currency_id') | ||||
|     { | ||||
|         $code = array_key_exists($key, $data) ? $data[$key] : false; | ||||
| 
 | ||||
|         return $this->maps['currencies'][$code] ?? $this->maps['company']->settings->currency_id; | ||||
|     } | ||||
| 
 | ||||
|     public function getClient($client_name, $client_email) | ||||
|     { | ||||
|         $clients = $this->maps['company']->clients; | ||||
| 
 | ||||
|         $client_id_search = $clients->where('id_number', $client_name); | ||||
| 
 | ||||
|         if ($client_id_search->count() >= 1) { | ||||
|             return $client_id_search->first()->id; | ||||
|         } | ||||
| 
 | ||||
|         $client_name_search = $clients->where('name', $client_name); | ||||
| 
 | ||||
|         if ($client_name_search->count() >= 1) { | ||||
|             return $client_name_search->first()->id; | ||||
|         } | ||||
| 
 | ||||
|         if (! empty($client_email)) { | ||||
|             $contacts = ClientContact::where('company_id', $this->maps['company']->id) | ||||
|                                      ->where('email', $client_email); | ||||
| 
 | ||||
|             if ($contacts->count() >= 1) { | ||||
|                 return $contacts->first()->client_id; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     ///////////////////////////////////////////////////////////////////////////////////
 | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function hasClient($name) | ||||
|     { | ||||
|         $name = trim(strtolower($name)); | ||||
| 
 | ||||
|         return isset($this->maps['client'][$name]); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function hasVendor($name) | ||||
|     { | ||||
|         $name = trim(strtolower($name)); | ||||
| 
 | ||||
|         return isset($this->maps['vendor'][$name]); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $key | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function hasProduct($key) | ||||
|     { | ||||
|         $key = trim(strtolower($key)); | ||||
| 
 | ||||
|         return isset($this->maps['product'][$key]); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $data | ||||
|      * @param $field | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     public function getNumber($data, $field) | ||||
|     { | ||||
|         return (isset($data->$field) && $data->$field) ? $data->$field : 0; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $data | ||||
|      * @param $field | ||||
|      * | ||||
|      * @return float | ||||
|      */ | ||||
|     public function getFloat($data, $field) | ||||
|     { | ||||
|         if (array_key_exists($field, $data)) { | ||||
|             $number = preg_replace('/[^0-9-.]+/', '', $data[$field]); | ||||
|         } else { | ||||
|             $number = 0; | ||||
|         } | ||||
| 
 | ||||
|         return Number::parseFloat($number); | ||||
|     } | ||||
| 
 | ||||
|     public function getFloatWithSamePrecision($data, $field) | ||||
|     { | ||||
|         $precision = (int) strpos(strrev($data[$field]), "."); | ||||
| 
 | ||||
|         return round($data[$field], $precision); | ||||
|     } | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getClientId($name) | ||||
|     { | ||||
|         $name = strtolower(trim($name)); | ||||
| 
 | ||||
|         return isset($this->maps['client'][$name]) ? $this->maps['client'][$name] : null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getProduct($data, $key, $field, $default = false) | ||||
|     { | ||||
|         $productKey = trim(strtolower($data->$key)); | ||||
| 
 | ||||
|         if (! isset($this->maps['product'][$productKey])) { | ||||
|             return $default; | ||||
|         } | ||||
| 
 | ||||
|         $product = $this->maps['product'][$productKey]; | ||||
| 
 | ||||
|         return $product->$field ?: $default; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getContact($email) | ||||
|     { | ||||
|         $email = trim(strtolower($email)); | ||||
| 
 | ||||
|         if (! isset($this->maps['contact'][$email])) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         return $this->maps['contact'][$email]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getCustomer($key) | ||||
|     { | ||||
|         $key = trim($key); | ||||
| 
 | ||||
|         if (! isset($this->maps['customer'][$key])) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         return $this->maps['customer'][$key]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getCountryId($name) | ||||
|     { | ||||
|         $name = strtolower(trim($name)); | ||||
| 
 | ||||
|         if (strlen($name) == 2) { | ||||
|             return $this->getCountryIdBy2($name); | ||||
|         } | ||||
| 
 | ||||
|         return isset($this->maps['countries'][$name]) ? $this->maps['countries'][$name] : null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getCountryIdBy2($name) | ||||
|     { | ||||
|         $name = strtolower(trim($name)); | ||||
| 
 | ||||
|         return isset($this->maps['countries2'][$name]) ? $this->maps['countries2'][$name] : null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getTaxRate($name) | ||||
|     { | ||||
|         $name = strtolower(trim($name)); | ||||
| 
 | ||||
|         return isset($this->maps['tax_rates'][$name]) ? $this->maps['tax_rates'][$name] : 0; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getTaxName($name) | ||||
|     { | ||||
|         $name = strtolower(trim($name)); | ||||
| 
 | ||||
|         return isset($this->maps['tax_names'][$name]) ? $this->maps['tax_names'][$name] : ''; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function getFirstName($name) | ||||
|     { | ||||
|         $name = Utils::splitName($name); | ||||
| 
 | ||||
|         return $name[0]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $date | ||||
|      * @param string $format | ||||
|      * @param mixed  $data | ||||
|      * @param mixed  $field | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getDate($data, $field) | ||||
|     { | ||||
|         if ($date = data_get($data, $field)) { | ||||
|             try { | ||||
|                 $date = new Carbon($date); | ||||
|             } catch (Exception $e) { | ||||
|                 // if we fail to parse return blank
 | ||||
|                 $date = false; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $date ? $date->format('Y-m-d') : null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function getLastName($name) | ||||
|     { | ||||
|         $name = Utils::splitName($name); | ||||
| 
 | ||||
|         return $name[1]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $number | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function getInvoiceNumber($number) | ||||
|     { | ||||
|         return $number ? ltrim(trim($number), '0') : null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $invoiceNumber | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getInvoiceId($invoiceNumber) | ||||
|     { | ||||
|         $invoiceNumber = $this->getInvoiceNumber($invoiceNumber); | ||||
|         $invoiceNumber = strtolower($invoiceNumber); | ||||
| 
 | ||||
|         return isset($this->maps['invoice'][$invoiceNumber]) ? $this->maps['invoice'][$invoiceNumber] : null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $invoiceNumber | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getInvoicePublicId($invoiceNumber) | ||||
|     { | ||||
|         $invoiceNumber = $this->getInvoiceNumber($invoiceNumber); | ||||
|         $invoiceNumber = strtolower($invoiceNumber); | ||||
| 
 | ||||
|         return isset($this->maps['invoice'][$invoiceNumber]) ? $this->maps['invoices'][$invoiceNumber]->public_id : null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $invoiceNumber | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function hasInvoice($invoiceNumber) | ||||
|     { | ||||
|         $invoiceNumber = $this->getInvoiceNumber($invoiceNumber); | ||||
|         $invoiceNumber = strtolower($invoiceNumber); | ||||
| 
 | ||||
|         return $this->maps['invoice'][$invoiceNumber] ?? null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $invoiceNumber | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getInvoiceClientId($invoiceNumber) | ||||
|     { | ||||
|         $invoiceNumber = $this->getInvoiceNumber($invoiceNumber); | ||||
|         $invoiceNumber = strtolower($invoiceNumber); | ||||
| 
 | ||||
|         return $this->maps['invoice_client'][$invoiceNumber] ?? null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getVendorId($name) | ||||
|     { | ||||
|         $name = strtolower(trim($name)); | ||||
| 
 | ||||
|         return $this->maps['vendor'][$name] ?? null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getExpenseCategoryId($name) | ||||
|     { | ||||
|         $name = strtolower(trim($name)); | ||||
| 
 | ||||
|         return $this->maps['expense_category'][$name] ?? null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getProjectId($name) | ||||
|     { | ||||
|         $name = strtolower(trim($name)); | ||||
| 
 | ||||
|         return $this->maps['project'][$name] ?? null; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param $name | ||||
|      * | ||||
|      * @return null | ||||
|      */ | ||||
|     public function getPaymentTypeId($name) | ||||
|     { | ||||
|         $name = strtolower(trim($name)); | ||||
| 
 | ||||
|         return $this->maps['payment_type'][$name] ?? null; | ||||
|     } | ||||
| } | ||||
| @ -1,78 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Invoice Ninja (https://invoiceninja.com). | ||||
|  * | ||||
|  * @link https://github.com/invoiceninja/invoiceninja source repository | ||||
|  * | ||||
|  * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) | ||||
|  * | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Import\Transformers; | ||||
| 
 | ||||
| use Illuminate\Support\Str; | ||||
| 
 | ||||
| /** | ||||
|  * Class ClientTransformer. | ||||
|  */ | ||||
| class ClientTransformer extends BaseTransformer | ||||
| { | ||||
|     /** | ||||
|      * @param $data | ||||
|      * | ||||
|      * @return bool|Item | ||||
|      */ | ||||
|     public function transform($data) | ||||
|     { | ||||
|         if (isset($data->name) && $this->hasClient($data->name)) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         $settings = new \stdClass; | ||||
|         $settings->currency_id = (string) $this->getCurrencyByCode($data); | ||||
| 
 | ||||
|         return [ | ||||
|             'company_id' => $this->maps['company']->id, | ||||
|             'name' => $this->getString($data, 'client.name'), | ||||
|             'phone' => $this->getString($data, 'client.phone'), | ||||
|             'address1' => $this->getString($data, 'client.address1'), | ||||
|             'address2' => $this->getString($data, 'client.address2'), | ||||
|             'city' => $this->getString($data, 'client.city'), | ||||
|             'state' => $this->getString($data, 'client.state'), | ||||
|             'shipping_address1' => $this->getString($data, 'client.shipping_address1'), | ||||
|             'shipping_address2' => $this->getString($data, 'client.shipping_address2'), | ||||
|             'shipping_city' => $this->getString($data, 'client.shipping_city'), | ||||
|             'shipping_state' => $this->getString($data, 'client.shipping_state'), | ||||
|             'shipping_postal_code' => $this->getString($data, 'client.shipping_postal_code'), | ||||
|             'public_notes' => $this->getString($data, 'client.public_notes'), | ||||
|             'private_notes' => $this->getString($data, 'client.private_notes'), | ||||
|             'website' => $this->getString($data, 'client.website'), | ||||
|             'vat_number' => $this->getString($data, 'client.vat_number'), | ||||
|             'id_number' => $this->getString($data, 'client.id_number'), | ||||
|             'custom_value1' => $this->getString($data, 'client.custom1'), | ||||
|             'custom_value2' => $this->getString($data, 'client.custom2'), | ||||
|             'custom_value3' => $this->getString($data, 'client.custom3'), | ||||
|             'custom_value4' => $this->getString($data, 'client.custom4'), | ||||
|             'balance' => $this->getFloat($data, 'client.balance'), | ||||
|             'paid_to_date' => $this->getFloat($data, 'client.paid_to_date'), | ||||
|             'credit_balance' => 0, | ||||
|             'settings' => $settings, | ||||
|             'client_hash' => Str::random(40), | ||||
|             'contacts' => [ | ||||
|                 [ | ||||
|                     'first_name' => $this->getString($data, 'contact.first_name'), | ||||
|                     'last_name' => $this->getString($data, 'contact.last_name'), | ||||
|                     'email' => $this->getString($data, 'contact.email'), | ||||
|                     'phone' => $this->getString($data, 'contact.phone'), | ||||
|                     'custom_value1' => $this->getString($data, 'contact.custom1'), | ||||
|                     'custom_value2' => $this->getString($data, 'contact.custom2'), | ||||
|                     'custom_value3' => $this->getString($data, 'contact.custom3'), | ||||
|                     'custom_value4' => $this->getString($data, 'contact.custom4'), | ||||
|                 ], | ||||
|             ], | ||||
|             'country_id' => isset($data->country_id) ? $this->getCountryId($data->country_id) : null, | ||||
|             'shipping_country_id' => isset($data->shipping_country_id) ? $this->getCountryId($data->shipping_country_id) : null, | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
| @ -1,120 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * client Ninja (https://clientninja.com). | ||||
|  * | ||||
|  * @link https://github.com/clientninja/clientninja source repository | ||||
|  * | ||||
|  * @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com) | ||||
|  * | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Import\Transformers\Csv; | ||||
| 
 | ||||
| use App\Import\ImportException; | ||||
| use App\Import\Transformers\BaseTransformer; | ||||
| use Illuminate\Support\Str; | ||||
| 
 | ||||
| /** | ||||
|  * Class ClientTransformer. | ||||
|  */ | ||||
| class ClientTransformer extends BaseTransformer | ||||
| { | ||||
|     /** | ||||
|      * @param $data | ||||
|      * | ||||
|      * @return array|bool | ||||
|      */ | ||||
|     public function transform($data) | ||||
|     { | ||||
|         if (isset($data->name) && $this->hasClient($data->name)) { | ||||
|             throw new ImportException('Client already exists'); | ||||
|         } | ||||
| 
 | ||||
|         $settings = new \stdClass(); | ||||
|         $settings->currency_id = (string) $this->getCurrencyByCode($data); | ||||
| 
 | ||||
|         return [ | ||||
|             'company_id' => $this->maps['company']->id, | ||||
|             'name' => $this->getString($data, 'client.name'), | ||||
|             'phone' => $this->getString($data, 'client.phone'), | ||||
|             'address1' => $this->getString($data, 'client.address1'), | ||||
|             'address2' => $this->getString($data, 'client.address2'), | ||||
|             'postal_code' => $this->getString($data, 'client.postal_code'), | ||||
|             'city' => $this->getString($data, 'client.city'), | ||||
|             'state' => $this->getString($data, 'client.state'), | ||||
|             'shipping_address1' => $this->getString( | ||||
|                 $data, | ||||
|                 'client.shipping_address1' | ||||
|             ), | ||||
|             'shipping_address2' => $this->getString( | ||||
|                 $data, | ||||
|                 'client.shipping_address2' | ||||
|             ), | ||||
|             'shipping_city' => $this->getString($data, 'client.shipping_city'), | ||||
|             'shipping_state' => $this->getString( | ||||
|                 $data, | ||||
|                 'client.shipping_state' | ||||
|             ), | ||||
|             'shipping_postal_code' => $this->getString( | ||||
|                 $data, | ||||
|                 'client.shipping_postal_code' | ||||
|             ), | ||||
|             'public_notes' => $this->getString($data, 'client.public_notes'), | ||||
|             'private_notes' => $this->getString($data, 'client.private_notes'), | ||||
|             'website' => $this->getString($data, 'client.website'), | ||||
|             'vat_number' => $this->getString($data, 'client.vat_number'), | ||||
|             'id_number' => $this->getString($data, 'client.id_number'), | ||||
|             'custom_value1' => $this->getString($data, 'client.custom_value1'), | ||||
|             'custom_value2' => $this->getString($data, 'client.custom_value2'), | ||||
|             'custom_value3' => $this->getString($data, 'client.custom_value3'), | ||||
|             'custom_value4' => $this->getString($data, 'client.custom_value4'), | ||||
|             'balance' => preg_replace( | ||||
|                 '/[^0-9,.]+/', | ||||
|                 '', | ||||
|                 $this->getFloat($data, 'client.balance') | ||||
|             ), | ||||
|             'paid_to_date' => preg_replace( | ||||
|                 '/[^0-9,.]+/', | ||||
|                 '', | ||||
|                 $this->getFloat($data, 'client.paid_to_date') | ||||
|             ), | ||||
|             'credit_balance' => 0, | ||||
|             'settings' => $settings, | ||||
|             'client_hash' => Str::random(40), | ||||
|             'contacts' => [ | ||||
|                 [ | ||||
|                     'first_name' => $this->getString( | ||||
|                         $data, | ||||
|                         'contact.first_name' | ||||
|                     ), | ||||
|                     'last_name' => $this->getString($data, 'contact.last_name'), | ||||
|                     'email' => $this->getString($data, 'contact.email'), | ||||
|                     'phone' => $this->getString($data, 'contact.phone'), | ||||
|                     'custom_value1' => $this->getString( | ||||
|                         $data, | ||||
|                         'contact.custom_value1' | ||||
|                     ), | ||||
|                     'custom_value2' => $this->getString( | ||||
|                         $data, | ||||
|                         'contact.custom_value2' | ||||
|                     ), | ||||
|                     'custom_value3' => $this->getString( | ||||
|                         $data, | ||||
|                         'contact.custom_value3' | ||||
|                     ), | ||||
|                     'custom_value4' => $this->getString( | ||||
|                         $data, | ||||
|                         'contact.custom_value4' | ||||
|                     ), | ||||
|                 ], | ||||
|             ], | ||||
|             'country_id' => isset($data['client.country']) | ||||
|                 ? $this->getCountryId($data['client.country']) | ||||
|                 : null, | ||||
|             'shipping_country_id' => isset($data['client.shipping_country']) | ||||
|                 ? $this->getCountryId($data['client.shipping_country']) | ||||
|                 : null, | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
| @ -1,42 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App\Import\Transformers\Csv; | ||||
| 
 | ||||
| use App\Import\Transformers\BaseTransformer; | ||||
| 
 | ||||
| /** | ||||
|  * Class InvoiceTransformer. | ||||
|  */ | ||||
| class ExpenseTransformer extends BaseTransformer | ||||
| { | ||||
|     /** | ||||
|      * @param $data | ||||
|      * | ||||
|      * @return bool|array | ||||
|      */ | ||||
|     public function transform($data) | ||||
|     { | ||||
|         $clientId = isset($data['expense.client']) ? $this->getClientId($data['expense.client']) : null; | ||||
| 
 | ||||
|         return [ | ||||
|             'company_id'            => $this->maps['company']->id, | ||||
|             'amount'                => $this->getFloat($data, 'expense.amount'), | ||||
|             'currency_id'           => $this->getCurrencyByCode($data, 'expense.currency_id'), | ||||
|             'vendor_id'             => isset($data['expense.vendor']) ? $this->getVendorId($data['expense.vendor']) : null, | ||||
|             'client_id'             => isset($data['expense.client']) ? $this->getClientId($data['expense.client']) : null, | ||||
|             'date'		            => isset($data['expense.date']) ? date('Y-m-d', strtotime($data['expense.date'])) : null, | ||||
|             'public_notes'          => $this->getString($data, 'expense.public_notes'), | ||||
|             'private_notes'         => $this->getString($data, 'expense.private_notes'), | ||||
|             'category_id'   		=> isset($data['expense.category']) ? $this->getExpenseCategoryId($data['expense.category']) : null, | ||||
|             'project_id'            => isset($data['expense.project']) ? $this->getProjectId($data['expense.project']) : null, | ||||
|             'payment_type_id'       => isset($data['expense.payment_type']) ? $this->getPaymentTypeId($data['expense.payment_type']) : null, | ||||
|             'payment_date'          => isset($data['expense.payment_date']) ? date('Y-m-d', strtotime($data['expense.payment_date'])) : null, | ||||
|             'custom_value1'        => $this->getString($data, 'expense.custom_value1'), | ||||
|             'custom_value2'        => $this->getString($data, 'expense.custom_value2'), | ||||
|             'custom_value3'        => $this->getString($data, 'expense.custom_value3'), | ||||
|             'custom_value4'        => $this->getString($data, 'expense.custom_value4'), | ||||
|             'transaction_reference' => $this->getString($data, 'expense.transaction_reference'), | ||||
|             'should_be_invoiced'    => $clientId ? true : false, | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
| @ -1,133 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * client Ninja (https://clientninja.com). | ||||
|  * | ||||
|  * @link https://github.com/clientninja/clientninja source repository | ||||
|  * | ||||
|  * @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com) | ||||
|  * | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Import\Transformers\Csv; | ||||
| 
 | ||||
| use App\Import\ImportException; | ||||
| use App\Import\Transformers\BaseTransformer; | ||||
| use App\Models\Invoice; | ||||
| 
 | ||||
| /** | ||||
|  * Class InvoiceTransformer. | ||||
|  */ | ||||
| class InvoiceTransformer extends BaseTransformer | ||||
| { | ||||
|     /** | ||||
|      * @param $data | ||||
|      * | ||||
|      * @return bool|array | ||||
|      */ | ||||
|     public function transform($line_items_data) | ||||
|     { | ||||
|         $invoice_data = reset($line_items_data); | ||||
| 
 | ||||
|         if ($this->hasInvoice($invoice_data['invoice.number'])) { | ||||
|             throw new ImportException('Invoice number already exists'); | ||||
|         } | ||||
| 
 | ||||
|         $invoiceStatusMap = [ | ||||
|             'sent'  => Invoice::STATUS_SENT, | ||||
|             'draft' => Invoice::STATUS_DRAFT, | ||||
|         ]; | ||||
| 
 | ||||
|         $transformed = [ | ||||
|             'company_id'        => $this->maps['company']->id, | ||||
|             'number'            => $this->getString($invoice_data, 'invoice.number'), | ||||
|             'user_id'           => $this->getString($invoice_data, 'invoice.user_id'), | ||||
|             'amount'            => $amount = $this->getFloat($invoice_data, 'invoice.amount'), | ||||
|             'balance'           => isset($invoice_data['invoice.balance']) ? $this->getFloat($invoice_data, 'invoice.balance') : $amount, | ||||
|             'client_id'         => $this->getClient($this->getString($invoice_data, 'client.name'), $this->getString($invoice_data, 'client.email')), | ||||
|             'discount'          => $this->getFloat($invoice_data, 'invoice.discount'), | ||||
|             'po_number'         => $this->getString($invoice_data, 'invoice.po_number'), | ||||
|             'date'              => isset($invoice_data['invoice.date']) ? date('Y-m-d', strtotime($invoice_data['invoice.date'])) : now()->format('Y-m-d'), | ||||
|             'due_date'          => isset($invoice_data['invoice.due_date']) ? date('Y-m-d', strtotime($invoice_data['invoice.due_date'])) : null, | ||||
|             'terms'             => $this->getString($invoice_data, 'invoice.terms'), | ||||
|             'public_notes'      => $this->getString($invoice_data, 'invoice.public_notes'), | ||||
|             // 'is_sent'           => $this->getString( $invoice_data, 'invoice.is_sent' ),
 | ||||
|             'private_notes'     => $this->getString($invoice_data, 'invoice.private_notes'), | ||||
|             'tax_name1'         => $this->getString($invoice_data, 'invoice.tax_name1'), | ||||
|             'tax_rate1'         => $this->getFloat($invoice_data, 'invoice.tax_rate1'), | ||||
|             'tax_name2'         => $this->getString($invoice_data, 'invoice.tax_name2'), | ||||
|             'tax_rate2'         => $this->getFloat($invoice_data, 'invoice.tax_rate2'), | ||||
|             'tax_name3'         => $this->getString($invoice_data, 'invoice.tax_name3'), | ||||
|             'tax_rate3'         => $this->getFloat($invoice_data, 'invoice.tax_rate3'), | ||||
|             'custom_value1'     => $this->getString($invoice_data, 'invoice.custom_value1'), | ||||
|             'custom_value2'     => $this->getString($invoice_data, 'invoice.custom_value2'), | ||||
|             'custom_value3'     => $this->getString($invoice_data, 'invoice.custom_value3'), | ||||
|             'custom_value4'     => $this->getString($invoice_data, 'invoice.custom_value4'), | ||||
|             'footer'            => $this->getString($invoice_data, 'invoice.footer'), | ||||
|             'partial'           => $this->getFloat($invoice_data, 'invoice.partial'), | ||||
|             'partial_due_date'  => $this->getString($invoice_data, 'invoice.partial_due_date'), | ||||
|             'custom_surcharge1' => $this->getString($invoice_data, 'invoice.custom_surcharge1'), | ||||
|             'custom_surcharge2' => $this->getString($invoice_data, 'invoice.custom_surcharge2'), | ||||
|             'custom_surcharge3' => $this->getString($invoice_data, 'invoice.custom_surcharge3'), | ||||
|             'custom_surcharge4' => $this->getString($invoice_data, 'invoice.custom_surcharge4'), | ||||
|             'exchange_rate'     => $this->getString($invoice_data, 'invoice.exchange_rate'), | ||||
|             'status_id'         => $invoiceStatusMap[$status = | ||||
|                     strtolower($this->getString($invoice_data, 'invoice.status'))] ?? | ||||
|                 Invoice::STATUS_SENT, | ||||
|             // 'viewed'            => $status === 'viewed',
 | ||||
|             'archived'          => $status === 'archived', | ||||
|         ]; | ||||
| 
 | ||||
|         if (isset($invoice_data['payment.amount'])) { | ||||
|             $transformed['payments'] = [ | ||||
|                 [ | ||||
|                     'date'                  => isset($invoice_data['payment.date']) ? date('Y-m-d', strtotime($invoice_data['payment.date'])) : date('y-m-d'), | ||||
|                     'transaction_reference' => $this->getString($invoice_data, 'payment.transaction_reference'), | ||||
|                     'amount'                => $this->getFloat($invoice_data, 'payment.amount'), | ||||
|                 ], | ||||
|             ]; | ||||
|         } elseif ($status === 'paid') { | ||||
|             $transformed['payments'] = [ | ||||
|                 [ | ||||
|                     'date'                  => isset($invoice_data['payment.date']) ? date('Y-m-d', strtotime($invoice_data['payment.date'])) : date('y-m-d'), | ||||
|                     'transaction_reference' => $this->getString($invoice_data, 'payment.transaction_reference'), | ||||
|                     'amount'                => $this->getFloat($invoice_data, 'invoice.amount'), | ||||
|                 ], | ||||
|             ]; | ||||
|         } elseif (isset($transformed['amount']) && isset($transformed['balance']) && ($transformed['amount'] != $transformed['balance'])) { | ||||
|             $transformed['payments'] = [ | ||||
|                 [ | ||||
|                     'date'                  => isset($invoice_data['payment.date']) ? date('Y-m-d', strtotime($invoice_data['payment.date'])) : date('y-m-d'), | ||||
|                     'transaction_reference' => $this->getString($invoice_data, 'payment.transaction_reference'), | ||||
|                     'amount'                => $transformed['amount'] - $transformed['balance'], | ||||
|                 ], | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|         $line_items = []; | ||||
|         foreach ($line_items_data as $record) { | ||||
|             $line_items[] = [ | ||||
|                 'quantity'           => $this->getFloat($record, 'item.quantity'), | ||||
|                 'cost'               => $this->getFloat($record, 'item.cost'), | ||||
|                 'product_key'        => $this->getString($record, 'item.product_key'), | ||||
|                 'notes'              => $this->getString($record, 'item.notes'), | ||||
|                 'discount'           => $this->getFloat($record, 'item.discount'), | ||||
|                 'is_amount_discount' => filter_var($this->getString($record, 'item.is_amount_discount'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE), | ||||
|                 'tax_name1'          => $this->getString($record, 'item.tax_name1'), | ||||
|                 'tax_rate1'          => $this->getFloat($record, 'item.tax_rate1'), | ||||
|                 'tax_name2'          => $this->getString($record, 'item.tax_name2'), | ||||
|                 'tax_rate2'          => $this->getFloat($record, 'item.tax_rate2'), | ||||
|                 'tax_name3'          => $this->getString($record, 'item.tax_name3'), | ||||
|                 'tax_rate3'          => $this->getFloat($record, 'item.tax_rate3'), | ||||
|                 'custom_value1'      => $this->getString($record, 'item.custom_value1'), | ||||
|                 'custom_value2'      => $this->getString($record, 'item.custom_value2'), | ||||
|                 'custom_value3'      => $this->getString($record, 'item.custom_value3'), | ||||
|                 'custom_value4'      => $this->getString($record, 'item.custom_value4'), | ||||
|                 'type_id'            => $this->getInvoiceTypeId($record, 'item.type_id'), | ||||
|             ]; | ||||
|         } | ||||
|         $transformed['line_items'] = $line_items; | ||||
| 
 | ||||
|         return $transformed; | ||||
|     } | ||||
| } | ||||
| @ -1,65 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * client Ninja (https://clientninja.com). | ||||
|  * | ||||
|  * @link https://github.com/clientninja/clientninja source repository | ||||
|  * | ||||
|  * @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com) | ||||
|  * | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Import\Transformers\Csv; | ||||
| 
 | ||||
| use App\Import\ImportException; | ||||
| use App\Import\Transformers\BaseTransformer; | ||||
| 
 | ||||
| /** | ||||
|  * Class PaymentTransformer. | ||||
|  */ | ||||
| class PaymentTransformer extends BaseTransformer | ||||
| { | ||||
|     /** | ||||
|      * @param $data | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function transform($data) | ||||
|     { | ||||
|         $client_id = | ||||
|             $this->getClient($this->getString($data, 'payment.client_id'), $this->getString($data, 'payment.client_id')); | ||||
| 
 | ||||
|         if (empty($client_id)) { | ||||
|             throw new ImportException('Could not find client.'); | ||||
|         } | ||||
| 
 | ||||
|         $transformed = [ | ||||
|             'company_id'            => $this->maps['company']->id, | ||||
|             'number'                => $this->getString($data, 'payment.number'), | ||||
|             'user_id'               => $this->getString($data, 'payment.user_id'), | ||||
|             'amount'                => $this->getFloat($data, 'payment.amount'), | ||||
|             'refunded'              => $this->getFloat($data, 'payment.refunded'), | ||||
|             'applied'               => $this->getFloat($data, 'payment.applied'), | ||||
|             'transaction_reference' => $this->getString($data, 'payment.transaction_reference '), | ||||
|             'date'                  => $this->getString($data, 'payment.date'), | ||||
|             'private_notes'         => $this->getString($data, 'payment.private_notes'), | ||||
|             'custom_value1'         => $this->getString($data, 'payment.custom_value1'), | ||||
|             'custom_value2'         => $this->getString($data, 'payment.custom_value2'), | ||||
|             'custom_value3'         => $this->getString($data, 'payment.custom_value3'), | ||||
|             'custom_value4'         => $this->getString($data, 'payment.custom_value4'), | ||||
|             'client_id'             => $client_id, | ||||
|         ]; | ||||
| 
 | ||||
|         if (isset($data['payment.invoice_number']) && | ||||
|             $invoice_id = $this->getInvoiceId($data['payment.invoice_number'])) { | ||||
|             $transformed['invoices'] = [ | ||||
|                 [ | ||||
|                     'invoice_id' => $invoice_id, | ||||
|                     'amount'     => $transformed['amount'] ?? null, | ||||
|                 ], | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|         return $transformed; | ||||
|     } | ||||
| } | ||||
| @ -1,47 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Invoice Ninja (https://invoiceninja.com). | ||||
|  * | ||||
|  * @link https://github.com/invoiceninja/invoiceninja source repository | ||||
|  * | ||||
|  * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) | ||||
|  * | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Import\Transformers\Csv; | ||||
| 
 | ||||
| use App\Import\Transformers\BaseTransformer; | ||||
| 
 | ||||
| /** | ||||
|  * Class ProductTransformer. | ||||
|  */ | ||||
| class ProductTransformer extends BaseTransformer | ||||
| { | ||||
|     /** | ||||
|      * @param $data | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function transform($data) | ||||
|     { | ||||
|         return [ | ||||
|             'company_id' => $this->maps['company']->id, | ||||
|             'product_key' => $this->getString($data, 'product.product_key'), | ||||
|             'notes' => $this->getString($data, 'product.notes'), | ||||
|             'cost' => $this->getFloat($data, 'product.cost'), | ||||
|             'price' => $this->getFloat($data, 'product.price'), | ||||
|             'quantity' => $this->getFloat($data, 'product.quantity'), | ||||
|             'tax_name1' => $this->getString($data, 'product.tax_name1'), | ||||
|             'tax_rate1' => $this->getFloat($data, 'product.tax_rate1'), | ||||
|             'tax_name2' => $this->getString($data, 'product.tax_name2'), | ||||
|             'tax_rate2' => $this->getFloat($data, 'product.tax_rate2'), | ||||
|             'tax_name3' => $this->getString($data, 'product.tax_name3'), | ||||
|             'tax_rate3' => $this->getFloat($data, 'product.tax_rate3'), | ||||
|             'custom_value1' => $this->getString($data, 'product.custom_value1'), | ||||
|             'custom_value2' => $this->getString($data, 'product.custom_value2'), | ||||
|             'custom_value3' => $this->getString($data, 'product.custom_value3'), | ||||
|             'custom_value4' => $this->getString($data, 'product.custom_value4'), | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
| @ -1,54 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace App\Import\Transformers\Csv; | ||||
| 
 | ||||
| use App\Import\ImportException; | ||||
| use App\Import\Transformers\BaseTransformer; | ||||
| 
 | ||||
| /** | ||||
|  * Class VendorTransformer. | ||||
|  */ | ||||
| class VendorTransformer extends BaseTransformer | ||||
| { | ||||
|     /** | ||||
|      * @param $data | ||||
|      * | ||||
|      * @return array|bool | ||||
|      */ | ||||
|     public function transform($data) | ||||
|     { | ||||
|         if (isset($data->name) && $this->hasVendor($data->name)) { | ||||
|             throw new ImportException('Vendor already exists'); | ||||
|         } | ||||
| 
 | ||||
|         return [ | ||||
|             'company_id' => $this->maps['company']->id, | ||||
|             'name'            => $this->getString($data, 'vendor.name'), | ||||
|             'phone'           => $this->getString($data, 'vendor.phone'), | ||||
|             'id_number'       => $this->getString($data, 'vendor.id_number'), | ||||
|             'vat_number'      => $this->getString($data, 'vendor.vat_number'), | ||||
|             'website'         => $this->getString($data, 'vendor.website'), | ||||
|             'currency_id'     => $this->getCurrencyByCode($data, 'vendor.currency_id'), | ||||
|             'public_notes'    => $this->getString($data, 'vendor.public_notes'), | ||||
|             'private_notes'   => $this->getString($data, 'vendor.private_notes'), | ||||
|             'address1'        => $this->getString($data, 'vendor.address1'), | ||||
|             'address2'        => $this->getString($data, 'vendor.address2'), | ||||
|             'city'            => $this->getString($data, 'vendor.city'), | ||||
|             'state'           => $this->getString($data, 'vendor.state'), | ||||
|             'postal_code'     => $this->getString($data, 'vendor.postal_code'), | ||||
|             'custom_value1'        => $this->getString($data, 'vendor.custom_value1'), | ||||
|             'custom_value2'        => $this->getString($data, 'vendor.custom_value2'), | ||||
|             'custom_value3'        => $this->getString($data, 'vendor.custom_value3'), | ||||
|             'custom_value4'        => $this->getString($data, 'vendor.custom_value4'), | ||||
|             'vendor_contacts' => [ | ||||
|                 [ | ||||
|                     'first_name' => $this->getString($data, 'vendor.first_name'), | ||||
|                     'last_name'  => $this->getString($data, 'vendor.last_name'), | ||||
|                     'email'      => $this->getString($data, 'vendor.email'), | ||||
|                     'phone'      => $this->getString($data, 'vendor.phone'), | ||||
|                 ], | ||||
|             ], | ||||
|             'country_id'      => isset($data['vendor.country_id']) ? $this->getCountryId($data['vendor.country_id']) : null, | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
| @ -1,57 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Invoice Ninja (https://clientninja.com). | ||||
|  * | ||||
|  * @link https://github.com/clientninja/clientninja source repository | ||||
|  * | ||||
|  * @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com) | ||||
|  * | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Import\Transformers\Freshbooks; | ||||
| 
 | ||||
| use App\Import\ImportException; | ||||
| use App\Import\Transformers\BaseTransformer; | ||||
| use Illuminate\Support\Str; | ||||
| 
 | ||||
| /** | ||||
|  * Class ClientTransformer. | ||||
|  */ | ||||
| class ClientTransformer extends BaseTransformer | ||||
| { | ||||
|     /** | ||||
|      * @param $data | ||||
|      * | ||||
|      * @return array|bool | ||||
|      */ | ||||
|     public function transform($data) | ||||
|     { | ||||
|         if (isset($data['Organization']) && $this->hasClient($data['Organization'])) { | ||||
|             throw new ImportException('Client already exists'); | ||||
|         } | ||||
| 
 | ||||
|         return [ | ||||
|             'company_id'     => $this->maps['company']->id, | ||||
|             'name'           => $this->getString($data, 'Organization'), | ||||
|             'phone'     => $this->getString($data, 'Phone'), | ||||
|             'address1'       => $this->getString($data, 'Street'), | ||||
|             'city'           => $this->getString($data, 'City'), | ||||
|             'state'          => $this->getString($data, 'Province/State'), | ||||
|             'postal_code'    => $this->getString($data, 'Postal Code'), | ||||
|             'country_id'     => isset($data['Country']) ? $this->getCountryId($data['Country']) : null, | ||||
|             'private_notes'   => $this->getString($data, 'Notes'), | ||||
|             'credit_balance' => 0, | ||||
|             'settings'       => new \stdClass, | ||||
|             'client_hash'    => Str::random(40), | ||||
|             'contacts'       => [ | ||||
|                 [ | ||||
|                     'first_name'    => $this->getString($data, 'First Name'), | ||||
|                     'last_name'     => $this->getString($data, 'Last Name'), | ||||
|                     'email'         => $this->getString($data, 'Email'), | ||||
|                     'phone'         => $this->getString($data, 'Phone'), | ||||
|                 ], | ||||
|             ], | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
| @ -1,87 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * client Ninja (https://clientninja.com). | ||||
|  * | ||||
|  * @link https://github.com/clientninja/clientninja source repository | ||||
|  * | ||||
|  * @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com) | ||||
|  * | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Import\Transformers\Freshbooks; | ||||
| 
 | ||||
| use App\Import\ImportException; | ||||
| use App\Import\Transformers\BaseTransformer; | ||||
| use App\Models\Invoice; | ||||
| use App\Utils\Number; | ||||
| 
 | ||||
| /** | ||||
|  * Class InvoiceTransformer. | ||||
|  */ | ||||
| class InvoiceTransformer extends BaseTransformer | ||||
| { | ||||
|     /** | ||||
|      * @param $line_items_data | ||||
|      * | ||||
|      * @return bool|array | ||||
|      */ | ||||
|     public function transform($line_items_data) | ||||
|     { | ||||
|         $invoice_data = reset($line_items_data); | ||||
| 
 | ||||
|         if ($this->hasInvoice($invoice_data['Invoice #'])) { | ||||
|             throw new ImportException('Invoice number already exists'); | ||||
|         } | ||||
| 
 | ||||
|         $invoiceStatusMap = [ | ||||
|             'sent'  => Invoice::STATUS_SENT, | ||||
|             'draft' => Invoice::STATUS_DRAFT, | ||||
|         ]; | ||||
| 
 | ||||
|         $transformed = [ | ||||
|             'company_id'  => $this->maps['company']->id, | ||||
|             'client_id'   => $this->getClient($this->getString($invoice_data, 'Client Name'), null), | ||||
|             'number'      => $this->getString($invoice_data, 'Invoice #'), | ||||
|             'date'        => isset($invoice_data['Date Issued']) ? date('Y-m-d', strtotime($invoice_data['Date Issued'])) : null, | ||||
|             'currency_id' => $this->getCurrencyByCode($invoice_data, 'Currency'), | ||||
|             'amount'      => 0, | ||||
|             'status_id'   => $invoiceStatusMap[$status = | ||||
|                     strtolower($this->getString($invoice_data, 'Invoice Status'))] ?? Invoice::STATUS_SENT, | ||||
|             // 'viewed'      => $status === 'viewed',
 | ||||
|         ]; | ||||
| 
 | ||||
|         $line_items = []; | ||||
|         foreach ($line_items_data as $record) { | ||||
|             $line_items[] = [ | ||||
|                 'product_key'        => $this->getString($record, 'Item Name'), | ||||
|                 'notes'              => $this->getString($record, 'Item Description'), | ||||
|                 'cost'               => $this->getFreshbookQuantityFloat($record, 'Rate'), | ||||
|                 'quantity'           => $this->getFreshbookQuantityFloat($record, 'Quantity'), | ||||
|                 'discount'           => $this->getFreshbookQuantityFloat($record, 'Discount Percentage'), | ||||
|                 'is_amount_discount' => false, | ||||
|                 'tax_name1'          => $this->getString($record, 'Tax 1 Type'), | ||||
|                 'tax_rate1'          => $this->getFreshbookQuantityFloat($record, 'Tax 1 Amount'), | ||||
|                 'tax_name2'          => $this->getString($record, 'Tax 2 Type'), | ||||
|                 'tax_rate2'          => $this->getFreshbookQuantityFloat($record, 'Tax 2 Amount'), | ||||
|             ]; | ||||
|             $transformed['amount'] += $this->getFreshbookQuantityFloat($record, 'Line Total'); | ||||
|         } | ||||
|         $transformed['line_items'] = $line_items; | ||||
| 
 | ||||
|         if (! empty($invoice_data['Date Paid'])) { | ||||
|             $transformed['payments'] = [[ | ||||
|                 'date'   => date('Y-m-d', strtotime($invoice_data['Date Paid'])), | ||||
|                 'amount' => $transformed['amount'], | ||||
|             ]]; | ||||
|         } | ||||
| 
 | ||||
|         return $transformed; | ||||
|     } | ||||
| 
 | ||||
|     /** @return float  */ | ||||
|     public function getFreshbookQuantityFloat($data, $field) | ||||
|     { | ||||
|         return $data[$field]; | ||||
|     } | ||||
| } | ||||
| @ -1,91 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * client Ninja (https://clientninja.com). | ||||
|  * | ||||
|  * @link https://github.com/clientninja/clientninja source repository | ||||
|  * | ||||
|  * @copyright Copyright (c) 2022. client Ninja LLC (https://clientninja.com) | ||||
|  * | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Import\Transformers\Invoice2Go; | ||||
| 
 | ||||
| use App\Import\ImportException; | ||||
| use App\Import\Transformers\BaseTransformer; | ||||
| use App\Models\Invoice; | ||||
| use Illuminate\Support\Str; | ||||
| 
 | ||||
| /** | ||||
|  * Class InvoiceTransformer. | ||||
|  */ | ||||
| class InvoiceTransformer extends BaseTransformer | ||||
| { | ||||
|     /** | ||||
|      * @param $line_items_data | ||||
|      * | ||||
|      * @return bool|array | ||||
|      */ | ||||
|     public function transform($invoice_data) | ||||
|     { | ||||
|         if ($this->hasInvoice($invoice_data['DocumentNumber'])) { | ||||
|             throw new ImportException('Invoice number already exists'); | ||||
|         } | ||||
| 
 | ||||
|         $invoiceStatusMap = [ | ||||
|             'unsent' => Invoice::STATUS_DRAFT, | ||||
|             'sent'   => Invoice::STATUS_SENT, | ||||
|         ]; | ||||
| 
 | ||||
|         $transformed = [ | ||||
|             'company_id'  => $this->maps['company']->id, | ||||
|             'number'      => $this->getString($invoice_data, 'DocumentNumber'), | ||||
|             'notes'       => $this->getString($invoice_data, 'Comment'), | ||||
|             'date'        => isset($invoice_data['DocumentDate']) ? date('Y-m-d', strtotime($invoice_data['DocumentDate'])) : null, | ||||
|             'currency_id' => $this->getCurrencyByCode($invoice_data, 'Currency'), | ||||
|             'amount'      => 0, | ||||
|             'status_id'   => $invoiceStatusMap[$status = | ||||
|                     strtolower($this->getString($invoice_data, 'DocumentStatus'))] ?? Invoice::STATUS_SENT, | ||||
|             // 'viewed'      => $status === 'viewed',
 | ||||
|             'line_items'  => [ | ||||
|                 [ | ||||
|                     'amount'             => $amount = $this->getFloat($invoice_data, 'TotalAmount'), | ||||
|                     'quantity'           => 1, | ||||
|                     'discount'           => $this->getFloat($invoice_data, 'DiscountValue'), | ||||
|                     'is_amount_discount' => false, | ||||
|                 ], | ||||
|             ], | ||||
|         ]; | ||||
| 
 | ||||
|         $client_id = | ||||
|             $this->getClient($this->getString($invoice_data, 'Name'), $this->getString($invoice_data, 'EmailRecipient')); | ||||
| 
 | ||||
|         if ($client_id) { | ||||
|             $transformed['client_id'] = $client_id; | ||||
|         } else { | ||||
|             $transformed['client'] = [ | ||||
|                 'name'              => $this->getString($invoice_data, 'Name'), | ||||
|                 'address1'          => $this->getString($invoice_data, 'DocumentRecipientAddress'), | ||||
|                 'shipping_address1' => $this->getString($invoice_data, 'ShipAddress'), | ||||
|                 'credit_balance'    => 0, | ||||
|                 'settings'          => new \stdClass, | ||||
|                 'client_hash'       => Str::random(40), | ||||
|                 'contacts'          => [ | ||||
|                     [ | ||||
|                         'email' => $this->getString($invoice_data, 'Email'), | ||||
|                     ], | ||||
|                 ], | ||||
|             ]; | ||||
|         } | ||||
|         if (! empty($invoice_data['Date Paid'])) { | ||||
|             $transformed['payments'] = [ | ||||
|                 [ | ||||
|                     'date'   => date('Y-m-d', strtotime($invoice_data['DatePaid'])), | ||||
|                     'amount' => $this->getFloat($invoice_data, 'Payments'), | ||||
|                 ], | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|         return $transformed; | ||||
|     } | ||||
| } | ||||
| @ -1,46 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Invoice Ninja (https://invoiceninja.com). | ||||
|  * | ||||
|  * @link https://github.com/invoiceninja/invoiceninja source repository | ||||
|  * | ||||
|  * @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com) | ||||
|  * | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Import\Transformers; | ||||
| 
 | ||||
| /** | ||||
|  * Class InvoiceItemTransformer. | ||||
|  */ | ||||
| class InvoiceItemTransformer extends BaseTransformer | ||||
| { | ||||
|     /** | ||||
|      * @param $data | ||||
|      * | ||||
|      * @return bool|Item | ||||
|      */ | ||||
|     public function transform($data) | ||||
|     { | ||||
|         return [ | ||||
|             'quantity' => $this->getFloat($data, 'item.quantity'), | ||||
|             'cost' => $this->getFloat($data, 'item.cost'), | ||||
|             'product_key' => $this->getString($data, 'item.product_key'), | ||||
|             'notes' => $this->getString($data, 'item.notes'), | ||||
|             'discount' => $this->getFloat($data, 'item.discount'), | ||||
|             'is_amount_discount' => $this->getString($data, 'item.is_amount_discount'), | ||||
|             'tax_name1' => $this->getString($data, 'item.tax_name1'), | ||||
|             'tax_rate1' => $this->getFloat($data, 'item.tax_rate1'), | ||||
|             'tax_name2' => $this->getString($data, 'item.tax_name2'), | ||||
|             'tax_rate2' => $this->getFloat($data, 'item.tax_rate2'), | ||||
|             'tax_name3' => $this->getString($data, 'item.tax_name3'), | ||||
|             'tax_rate3' => $this->getFloat($data, 'item.tax_rate3'), | ||||
|             'custom_value1' => $this->getString($data, 'item.custom_value1'), | ||||
|             'custom_value2' => $this->getString($data, 'item.custom_value2'), | ||||
|             'custom_value3' => $this->getString($data, 'item.custom_value3'), | ||||
|             'custom_value4' => $this->getString($data, 'item.custom_value4'), | ||||
|             'type_id' => $this->getInvoiceTypeId($data, 'item.type_id'), | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user