mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-01 20:17:35 -04:00 
			
		
		
		
	Merge branch 'v5-develop' into vite-migration
This commit is contained in:
		
						commit
						99b1aab2a2
					
				
							
								
								
									
										8
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @ -19,7 +19,7 @@ jobs: | ||||
|         uses: actions/checkout@v1 | ||||
|         with: | ||||
|           ref: v5-develop | ||||
|            | ||||
|        | ||||
|       - name: Copy .env file | ||||
|         run: | | ||||
|           cp .env.example .env | ||||
| @ -46,7 +46,11 @@ jobs: | ||||
|           git checkout main | ||||
|           npm i | ||||
|           npm run build | ||||
|           cp -r dist/react/* ../public/react  | ||||
|            | ||||
|           mkdir -p ../public/react/${{ github.event.release.tag_name }}/ | ||||
|           cp -r dist/react/* ../public/react/${{ github.event.release.tag_name }}/ | ||||
|           cp -r dist/react/* ../public/react/ | ||||
| 
 | ||||
|           mkdir -p ../public/tinymce_6.4.2/tinymce/js/ | ||||
|           cp -r node_modules/tinymce ../public/tinymce_6.4.2/tinymce/js/ | ||||
|           cd .. | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| 5.7.7 | ||||
| 5.7.22 | ||||
| @ -862,7 +862,7 @@ class CheckData extends Command | ||||
|                 } | ||||
|                 $records = DB::table($table) | ||||
|                                 ->join($tableName, "{$tableName}.id", '=', "{$table}.{$field}_id") | ||||
|                                 ->where("{$table}.{$company_id}", '!=', "{$tableName}.company_id") | ||||
|                                 ->whereRaw("{$table}.{$company_id} != {$tableName}.company_id") | ||||
|                                 ->get(["{$table}.id"]); | ||||
| 
 | ||||
|                 if ($records->count()) { | ||||
|  | ||||
| @ -61,6 +61,8 @@ class CreateTestData extends Command | ||||
| 
 | ||||
|     protected $invoice_repo; | ||||
| 
 | ||||
|     protected $count; | ||||
|      | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|  | ||||
| @ -48,7 +48,7 @@ class ReactBuilder extends Command | ||||
|     { | ||||
|         $includes = ''; | ||||
| 
 | ||||
|         $directoryIterator = new \RecursiveDirectoryIterator(public_path('react'), \RecursiveDirectoryIterator::SKIP_DOTS); | ||||
|         $directoryIterator = new \RecursiveDirectoryIterator(public_path('react/v'.config('ninja.app_version').'/'), \RecursiveDirectoryIterator::SKIP_DOTS); | ||||
| 
 | ||||
|         foreach (new \RecursiveIteratorIterator($directoryIterator) as $file) { | ||||
|             if ($file->getExtension() == 'js') { | ||||
|  | ||||
| @ -173,7 +173,7 @@ class SendRemindersCron extends Command | ||||
|         /**Refresh Invoice values*/ | ||||
|         $invoice->calc()->getInvoice()->save(); | ||||
|         $invoice->fresh(); | ||||
|         $invoice->service()->deletePdf()->save(); | ||||
|         // $invoice->service()->deletePdf()->save();
 | ||||
|         if ($invoice->client->getSetting('enable_e_invoice')){ | ||||
|             $invoice->service()->deleteEInvoice()->save(); | ||||
|         } | ||||
|  | ||||
| @ -11,28 +11,29 @@ | ||||
| 
 | ||||
| namespace App\Console; | ||||
| 
 | ||||
| use App\Jobs\Cron\AutoBillCron; | ||||
| use App\Jobs\Cron\RecurringExpensesCron; | ||||
| use App\Jobs\Cron\RecurringInvoicesCron; | ||||
| use App\Jobs\Cron\SubscriptionCron; | ||||
| use App\Jobs\Cron\UpdateCalculatedFields; | ||||
| use App\Jobs\Invoice\InvoiceCheckLateWebhook; | ||||
| use App\Jobs\Ninja\AdjustEmailQuota; | ||||
| use App\Jobs\Ninja\BankTransactionSync; | ||||
| use App\Jobs\Ninja\CompanySizeCheck; | ||||
| use App\Utils\Ninja; | ||||
| use App\Models\Account; | ||||
| use App\Jobs\Ninja\QueueSize; | ||||
| use App\Jobs\Ninja\SystemMaintenance; | ||||
| use App\Jobs\Ninja\TaskScheduler; | ||||
| 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; | ||||
| use App\Jobs\Util\UpdateExchangeRates; | ||||
| use App\Jobs\Cron\AutoBillCron; | ||||
| use App\Jobs\Util\VersionCheck; | ||||
| use App\Models\Account; | ||||
| use App\Utils\Ninja; | ||||
| use App\Jobs\Ninja\TaskScheduler; | ||||
| use App\Jobs\Util\SchedulerCheck; | ||||
| use App\Jobs\Ninja\CheckACHStatus; | ||||
| use App\Jobs\Cron\SubscriptionCron; | ||||
| use App\Jobs\Ninja\AdjustEmailQuota; | ||||
| use App\Jobs\Ninja\CompanySizeCheck; | ||||
| use App\Jobs\Ninja\SystemMaintenance; | ||||
| use App\Jobs\Quote\QuoteCheckExpired; | ||||
| use App\Jobs\Util\UpdateExchangeRates; | ||||
| use App\Jobs\Ninja\BankTransactionSync; | ||||
| use App\Jobs\Cron\RecurringExpensesCron; | ||||
| use App\Jobs\Cron\RecurringInvoicesCron; | ||||
| use App\Jobs\Cron\UpdateCalculatedFields; | ||||
| use Illuminate\Console\Scheduling\Schedule; | ||||
| use App\Jobs\Invoice\InvoiceCheckLateWebhook; | ||||
| use App\Jobs\Subscription\CleanStaleInvoiceOrder; | ||||
| use Illuminate\Foundation\Console\Kernel as ConsoleKernel; | ||||
| 
 | ||||
| class Kernel extends ConsoleKernel | ||||
| @ -107,7 +108,10 @@ class Kernel extends ConsoleKernel | ||||
|             $schedule->job(new AdjustEmailQuota)->dailyAt('23:30')->withoutOverlapping(); | ||||
| 
 | ||||
|             /* Pulls in bank transactions from third party services */ | ||||
|             $schedule->job(new BankTransactionSync)->dailyAt('04:10')->withoutOverlapping()->name('bank-trans-sync-job')->onOneServer(); | ||||
|             $schedule->job(new BankTransactionSync)->everyFourHours()->withoutOverlapping()->name('bank-trans-sync-job')->onOneServer(); | ||||
| 
 | ||||
|             /* Checks ACH verification status and updates state to authorize when verified */ | ||||
|             $schedule->job(new CheckACHStatus)->everySixHours()->withoutOverlapping()->name('ach-status-job')->onOneServer(); | ||||
| 
 | ||||
|             $schedule->command('ninja:check-data --database=db-ninja-01')->dailyAt('02:10')->withoutOverlapping()->name('check-data-db-1-job')->onOneServer(); | ||||
| 
 | ||||
|  | ||||
| @ -481,8 +481,11 @@ class CompanySettings extends BaseSettings | ||||
| 
 | ||||
|     public $enable_e_invoice = false; | ||||
| 
 | ||||
|     public $classification = ''; // individual, company, partnership, trust, charity, government, other
 | ||||
| 
 | ||||
|     public static $casts = [ | ||||
|         'enable_e_invoice'                   => 'bool',  | ||||
|         'classification'                     => 'string', | ||||
|         'default_expense_payment_type_id'    => 'string', | ||||
|         'e_invoice_type'                     => 'string',     | ||||
|         'mailgun_endpoint'                   => 'string',     | ||||
| @ -843,6 +846,23 @@ class CompanySettings extends BaseSettings | ||||
|         return $notification; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Stubs the notification defaults | ||||
|      * | ||||
|      * @return stdClass | ||||
|      */ | ||||
|     public static function notificationAdminDefaults() :stdClass | ||||
|     { | ||||
|         $notification = new stdClass; | ||||
|         $notification->email = []; | ||||
|         $notification->email = ['invoice_sent_all']; | ||||
| 
 | ||||
|         return $notification; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Defines entity variables for PDF generation | ||||
|      * | ||||
|  | ||||
| @ -363,7 +363,15 @@ class BaseRule implements RuleInterface | ||||
| 
 | ||||
|     public function override($item): self | ||||
|     { | ||||
|         $this->tax_rate1 = $item->tax_rate1; | ||||
|         $this->tax_name1 = $item->tax_name1; | ||||
|         $this->tax_rate2 = $item->tax_rate2; | ||||
|         $this->tax_name2 = $item->tax_name2; | ||||
|         $this->tax_rate3 = $item->tax_rate3; | ||||
|         $this->tax_name3 = $item->tax_name3; | ||||
| 
 | ||||
|         return $this; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public function calculateRates(): self | ||||
|  | ||||
| @ -49,6 +49,10 @@ class Rule extends BaseRule implements RuleInterface | ||||
|          | ||||
|         $this->tax_rate1 = $item->tax_rate1; | ||||
|         $this->tax_name1 = $item->tax_name1; | ||||
|         $this->tax_rate2 = $item->tax_rate2; | ||||
|         $this->tax_name2 = $item->tax_name2; | ||||
|         $this->tax_rate3 = $item->tax_rate3; | ||||
|         $this->tax_name3 = $item->tax_name3; | ||||
| 
 | ||||
|         return $this; | ||||
| 
 | ||||
|  | ||||
| @ -57,9 +57,11 @@ class ActivityExport extends BaseExport | ||||
|                 return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; | ||||
|             })->toArray(); | ||||
| 
 | ||||
|         $report = $query->cursor() | ||||
|                 ->map(function ($credit) { | ||||
|                     return $this->buildActivityRow($credit); | ||||
|              | ||||
|             $report = $query->cursor() | ||||
|                 ->map(function ($resource) { | ||||
|                     $row = $this->buildActivityRow($resource); | ||||
|                     return $this->processMetaData($row, $resource); | ||||
|                 })->toArray(); | ||||
|          | ||||
|         return array_merge(['columns' => $header], $report); | ||||
| @ -70,6 +72,8 @@ class ActivityExport extends BaseExport | ||||
|             return [ | ||||
|             Carbon::parse($activity->created_at)->format($this->date_format), | ||||
|             ctrans("texts.activity_{$activity->activity_type_id}",[ | ||||
|                 'payment_amount' => $activity->payment ? $activity->payment->amount : '', | ||||
|                 'adjustment' => $activity->payment ? $activity->payment->refunded : '', | ||||
|                 'client' => $activity->client ? $activity->client->present()->name() : '', | ||||
|                 'contact' => $activity->contact ? $activity->contact->present()->name() : '', | ||||
|                 'quote' => $activity->quote ? $activity->quote->number : '', | ||||
| @ -101,7 +105,7 @@ class ActivityExport extends BaseExport | ||||
| 
 | ||||
|         $this->date_format = DateFormat::find($this->company->settings->date_format_id)->format; | ||||
| 
 | ||||
|         ksort($this->entity_keys); | ||||
|         // ksort($this->entity_keys);
 | ||||
| 
 | ||||
|         if (count($this->input['report_keys']) == 0) { | ||||
|             $this->input['report_keys'] = array_values($this->entity_keys); | ||||
| @ -146,4 +150,27 @@ class ActivityExport extends BaseExport | ||||
|     { | ||||
|         return $entity; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public function processMetaData(array $row, $resource): array | ||||
|     { | ||||
| 
 | ||||
|         $clean_row = []; | ||||
|          | ||||
|         foreach (array_values($this->input['report_keys']) as $key => $value) { | ||||
|          | ||||
|             nlog("key: {$key}, value: {$value}"); | ||||
|             nlog($row); | ||||
|             $clean_row[$key]['entity'] = 'activity'; | ||||
|             $clean_row[$key]['id'] = $key; | ||||
|             $clean_row[$key]['hashed_id'] = null; | ||||
|             $clean_row[$key]['value'] = $row[$key]; | ||||
|             $clean_row[$key]['identifier'] = $value; | ||||
|             $clean_row[$key]['display_value'] = $row[$key]; | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         return $clean_row; | ||||
|     }    | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -11,20 +11,30 @@ | ||||
| 
 | ||||
| namespace App\Export\CSV; | ||||
| 
 | ||||
| use App\Models\Activity; | ||||
| use App\Models\Quote; | ||||
| use App\Utils\Number; | ||||
| use App\Models\Client; | ||||
| use App\Models\Credit; | ||||
| use App\Utils\Helpers; | ||||
| use App\Models\Company; | ||||
| use App\Models\Expense; | ||||
| use App\Models\Invoice; | ||||
| use App\Models\Payment; | ||||
| use App\Models\Document; | ||||
| use League\Fractal\Manager; | ||||
| use App\Models\ClientContact; | ||||
| use App\Models\PurchaseOrder; | ||||
| use App\Models\RecurringInvoice; | ||||
| use Illuminate\Support\Carbon; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use App\Transformers\TaskTransformer; | ||||
| use App\Transformers\PaymentTransformer; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use League\Fractal\Serializer\ArraySerializer; | ||||
| use App\Models\Product; | ||||
| use App\Models\Task; | ||||
| use App\Models\Vendor; | ||||
| 
 | ||||
| class BaseExport | ||||
| { | ||||
| @ -56,7 +66,7 @@ class BaseExport | ||||
|         'id_number' => 'vendor.id_number', | ||||
|         'name' => 'vendor.name', | ||||
|         'number' => 'vendor.number', | ||||
|         'client_phone' => 'vendor.phone', | ||||
|         'phone' => 'vendor.phone', | ||||
|         'postal_code' => 'vendor.postal_code', | ||||
|         'private_notes' => 'vendor.private_notes', | ||||
|         'public_notes' => 'vendor.public_notes', | ||||
| @ -229,8 +239,8 @@ class BaseExport | ||||
|     ]; | ||||
| 
 | ||||
|     protected array $product_report_keys  = [ | ||||
|         'project' => 'project_id', | ||||
|         'vendor' => 'vendor_id', | ||||
|         // 'project' => 'project_id',
 | ||||
|         // 'vendor' => 'vendor_id',
 | ||||
|         'custom_value1' => 'custom_value1', | ||||
|         'custom_value2' => 'custom_value2', | ||||
|         'custom_value3' => 'custom_value3', | ||||
| @ -246,6 +256,10 @@ class BaseExport | ||||
|         'tax_name1' => 'tax_name1', | ||||
|         'tax_name2' => 'tax_name2', | ||||
|         'tax_name3' => 'tax_name3', | ||||
|         'image' => 'product_image', | ||||
|         'tax_category' => 'tax_id', | ||||
|         'max_quantity' => 'max_quantity', | ||||
|         'in_stock_quantity' => 'in_stock_quantity', | ||||
|     ]; | ||||
| 
 | ||||
|     protected array $item_report_keys = [ | ||||
| @ -364,7 +378,7 @@ class BaseExport | ||||
|     protected array $expense_report_keys = [ | ||||
|         'amount' => 'expense.amount', | ||||
|         'category' => 'expense.category_id', | ||||
|         'client' => 'expense.client_id', | ||||
|         // 'client' => 'expense.client_id',
 | ||||
|         'custom_value1' => 'expense.custom_value1', | ||||
|         'custom_value2' => 'expense.custom_value2', | ||||
|         'custom_value3' => 'expense.custom_value3', | ||||
| @ -578,31 +592,33 @@ class BaseExport | ||||
|         $manager->setSerializer(new ArraySerializer()); | ||||
|         $transformed_client = $manager->createData($transformed_client)->toArray(); | ||||
| 
 | ||||
|         if($column == 'name') | ||||
|         if(in_array($column, ['client.name', 'name'])) | ||||
|             return $transformed_client['display_name']; | ||||
|          | ||||
|         if($column == 'user_id') | ||||
|         if(in_array($column, ['client.user_id', 'user_id'])) | ||||
|             return $entity->client->user->present()->name(); | ||||
| 
 | ||||
|         if($column == 'country_id') | ||||
|         if(in_array($column, ['client.assigned_user_id', 'assigned_user_id']))  | ||||
|             return $entity->client->assigned_user->present()->name(); | ||||
| 
 | ||||
|         if(in_array($column, ['client.country_id', 'country_id'])) | ||||
|             return $entity->client->country ? ctrans("texts.country_{$entity->client->country->name}") : ''; | ||||
|          | ||||
|         if($column == 'shipping_country_id') | ||||
|         if(in_array($column, ['client.shipping_country_id', 'shipping_country_id'])) | ||||
|             return $entity->client->shipping_country ? ctrans("texts.country_{$entity->client->shipping_country->name}") : ''; | ||||
|          | ||||
|         if($column == 'size_id') | ||||
|         if(in_array($column, ['client.size_id', 'size_id'])) | ||||
|             return $entity->client->size?->name ?? ''; | ||||
| 
 | ||||
|         if($column == 'industry_id') | ||||
|         if(in_array($column, ['client.industry_id', 'industry_id'])) | ||||
|             return $entity->client->industry?->name ?? ''; | ||||
| 
 | ||||
|         if ($column == 'currency_id') { | ||||
|         if (in_array($column, ['client.currency_id', 'currency_id'])) | ||||
|             return $entity->client->currency() ? $entity->client->currency()->code : $entity->company->currency()->code; | ||||
|         } | ||||
| 
 | ||||
|         if($column == 'client.payment_terms') { | ||||
|          | ||||
|         if(in_array($column, ['payment_terms', 'client.payment_terms'])) | ||||
|             return $entity->client->getSetting('payment_terms'); | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         if(array_key_exists($column, $transformed_client)) | ||||
|             return $transformed_client[$column]; | ||||
| @ -644,7 +660,7 @@ class BaseExport | ||||
|         // nlog("searching for {$column}");
 | ||||
|         $transformed_invoice = false; | ||||
| 
 | ||||
|         if($transformer instanceof PaymentTransformer) { | ||||
|         if($transformer instanceof PaymentTransformer && ($entity->invoices ?? false)) { | ||||
|             $transformed_invoices = $transformer->includeInvoices($entity); | ||||
| 
 | ||||
|             $manager = new Manager(); | ||||
| @ -666,7 +682,7 @@ class BaseExport | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         if($transformer instanceof TaskTransformer) { | ||||
|         if($transformer instanceof TaskTransformer && ($entity->invoice ?? false)) { | ||||
|             $transformed_invoice = $transformer->includeInvoice($entity); | ||||
| 
 | ||||
|             if(!$transformed_invoice) | ||||
| @ -900,7 +916,7 @@ class BaseExport | ||||
|         $helper = new Helpers(); | ||||
| 
 | ||||
|         $header = []; | ||||
| 
 | ||||
|         // nlog("header");
 | ||||
|         foreach ($this->input['report_keys'] as $value) { | ||||
|              | ||||
|             $key = array_search($value, $this->entity_keys); | ||||
| @ -947,6 +963,9 @@ class BaseExport | ||||
|             if(!$key) { | ||||
|                 $prefix = ctrans('texts.expense')." "; | ||||
|                 $key = array_search($value, $this->expense_report_keys); | ||||
|                  | ||||
|                     if(!$key && $value == 'expense.category') | ||||
|                         $key = 'category'; | ||||
|             } | ||||
| 
 | ||||
|             if(!$key) { | ||||
| @ -973,6 +992,8 @@ class BaseExport | ||||
|                 $prefix = ''; | ||||
|             } | ||||
| 
 | ||||
|             // nlog("key => {$key}");
 | ||||
| 
 | ||||
|             $key = str_replace('item.', '', $key); | ||||
|             $key = str_replace('recurring_invoice.', '', $key); | ||||
|             $key = str_replace('purchase_order.', '', $key); | ||||
| @ -986,6 +1007,7 @@ class BaseExport | ||||
|             $key = str_replace('payment.', '', $key); | ||||
|             $key = str_replace('expense.', '', $key); | ||||
|             $key = str_replace('product.', '', $key); | ||||
|             $key = str_replace('task.', '', $key); | ||||
| 
 | ||||
|             if(stripos($value, 'custom_value') !== false) | ||||
|             { | ||||
| @ -1004,12 +1026,15 @@ class BaseExport | ||||
|                     } | ||||
| 
 | ||||
|                 } | ||||
|                 elseif(count($parts) == 2 && stripos($parts[0], 'contact') !== false) { | ||||
|                 elseif(count($parts) == 2 && (stripos($parts[0], 'vendor_contact') !== false || stripos($parts[0], 'contact') !== false)) { | ||||
|                     $parts[0] = str_replace('vendor_contact', 'contact', $parts[0]); | ||||
| 
 | ||||
|                     $entity = "contact".substr($parts[1], -1); | ||||
|                     $custom_field_string = strlen($helper->makeCustomField($this->company->custom_fields, $entity)) > 1 ? $helper->makeCustomField($this->company->custom_fields, $entity) : ctrans("texts.{$parts[1]}"); | ||||
|                     $header[] = ctrans("texts.{$parts[0]}") . " " . $custom_field_string; | ||||
|                      | ||||
|                 } | ||||
|                 elseif(count($parts) == 2 && in_array(substr($original_key, 0, -1), ['credit','quote','invoice','purchase_order','recurring_invoice'])){ | ||||
|                 elseif(count($parts) == 2 && in_array(substr($original_key, 0, -1), ['credit','quote','invoice','purchase_order','recurring_invoice','task'])){ | ||||
|                     $custom_field_string = strlen($helper->makeCustomField($this->company->custom_fields, "product".substr($original_key,-1))) > 1 ? $helper->makeCustomField($this->company->custom_fields, "product".substr($original_key,-1)) : ctrans("texts.{$parts[1]}"); | ||||
|                     $header[] = ctrans("texts.{$parts[0]}") . " " . $custom_field_string; | ||||
|                 } | ||||
| @ -1028,4 +1053,100 @@ class BaseExport | ||||
|          | ||||
|         return $header; | ||||
|     } | ||||
| 
 | ||||
|     public function processMetaData(array $row, $resource): array | ||||
|     { | ||||
|         $class = get_class($resource); | ||||
| 
 | ||||
|         $entity = ''; | ||||
| 
 | ||||
|         match ($class) { | ||||
|             Invoice::class => $entity = 'invoice', | ||||
|             RecurringInvoice::class => $entity = 'recurring_invoice', | ||||
|             Quote::class => $entity = 'quote', | ||||
|             Credit::class => $entity = 'credit', | ||||
|             Expense::class => $entity = 'expense', | ||||
|             Document::class => $entity = 'document', | ||||
|             ClientContact::class => $entity = 'contact', | ||||
|             PurchaseOrder::class => $entity = 'purchase_order', | ||||
|             Payment::class => $entity = 'payment', | ||||
|             Product::class => $entity = 'product', | ||||
|             Task::class => $entity = 'task', | ||||
|             Vendor::class => $entity = 'vendor', | ||||
|             default => $entity = 'invoice', | ||||
|         }; | ||||
|          | ||||
|         $clean_row = []; | ||||
|          | ||||
|         foreach (array_values($this->input['report_keys']) as $key => $value) { | ||||
|          | ||||
|             $report_keys = explode(".", $value); | ||||
|              | ||||
|             $column_key = $value; | ||||
|              | ||||
|             if($value == 'product_image') { | ||||
|                 $column_key = 'image'; | ||||
|                 $value = 'image'; | ||||
|             } | ||||
| 
 | ||||
|             if($value == 'tax_id') {          | ||||
|                 $column_key = 'tax_category'; | ||||
|                 $value = 'tax_category'; | ||||
|             } | ||||
| 
 | ||||
|             $clean_row[$key]['entity'] = $report_keys[0]; | ||||
|             $clean_row[$key]['id'] = $report_keys[1] ?? $report_keys[0]; | ||||
|             $clean_row[$key]['hashed_id'] = $report_keys[0] == $entity ? null : $resource->{$report_keys[0]}->hashed_id ?? null; | ||||
|             $clean_row[$key]['value'] = $row[$column_key]; | ||||
|             $clean_row[$key]['identifier'] = $value; | ||||
|             $clean_row[$key]['display_value'] = $row[$column_key]; | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         return $clean_row; | ||||
|     }    | ||||
| 
 | ||||
|     public function processItemMetaData(array $row, $resource): array | ||||
|     { | ||||
|         $class = get_class($resource); | ||||
| 
 | ||||
|         $entity = ''; | ||||
| 
 | ||||
|         match ($class) { | ||||
|             Invoice::class => $entity = 'invoice', | ||||
|             Quote::class => $entity = 'quote', | ||||
|             Credit::class => $entity = 'credit', | ||||
|             Expense::class => $entity = 'expense', | ||||
|             Document::class => $entity = 'document', | ||||
|             ClientContact::class => $entity = 'contact', | ||||
|             PurchaseOrder::class => $entity = 'purchase_order', | ||||
|             default => $entity = 'invoice', | ||||
|         }; | ||||
| 
 | ||||
|         $clean_row = []; | ||||
| 
 | ||||
|         foreach (array_values($this->input['report_keys']) as $key => $value) { | ||||
|          | ||||
|             $report_keys = explode(".", $value); | ||||
|              | ||||
|             $column_key = $value; | ||||
| 
 | ||||
|             if($value == 'type_id' || $value == 'item.type_id') | ||||
|                 $column_key = 'type'; | ||||
| 
 | ||||
|             if($value == 'tax_id' || $value == 'item.tax_id') | ||||
|                 $column_key = 'tax_category'; | ||||
|                  | ||||
|             $clean_row[$key]['entity'] = $report_keys[0]; | ||||
|             $clean_row[$key]['id'] = $report_keys[1] ?? $report_keys[0]; | ||||
|             $clean_row[$key]['hashed_id'] = $report_keys[0] == $entity ? null : $resource->{$report_keys[0]}->hashed_id ?? null; | ||||
|             $clean_row[$key]['value'] = isset($row[$column_key]) ? $row[$column_key] : $row[$report_keys[1]]; | ||||
|             $clean_row[$key]['identifier'] = $value; | ||||
|             $clean_row[$key]['display_value'] = isset($row[$column_key]) ? $row[$column_key] : $row[$report_keys[1]]; | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         return $clean_row; | ||||
|     }    | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -11,15 +11,16 @@ | ||||
| 
 | ||||
| namespace App\Export\CSV; | ||||
| 
 | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Models\Client; | ||||
| use App\Models\Company; | ||||
| use App\Transformers\ClientContactTransformer; | ||||
| use App\Transformers\ClientTransformer; | ||||
| use App\Utils\Ninja; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use App\Utils\Number; | ||||
| use App\Models\Client; | ||||
| use League\Csv\Writer; | ||||
| use App\Models\Company; | ||||
| use App\Libraries\MultiDB; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use App\Transformers\ClientTransformer; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use App\Transformers\ClientContactTransformer; | ||||
| 
 | ||||
| class ClientExport extends BaseExport | ||||
| { | ||||
| @ -93,7 +94,8 @@ class ClientExport extends BaseExport | ||||
| 
 | ||||
|         $report = $query->cursor() | ||||
|                 ->map(function ($client) { | ||||
|                     return $this->buildRow($client); | ||||
|                     $row = $this->buildRow($client); | ||||
|                     return $this->processMetaData($row, $client); | ||||
|                 })->toArray(); | ||||
|          | ||||
|         return array_merge(['columns' => $header], $report); | ||||
| @ -171,6 +173,30 @@ class ClientExport extends BaseExport | ||||
|         return $this->decorateAdvancedFields($client, $entity); | ||||
|     } | ||||
| 
 | ||||
|     public function processMetaData(array $row, $resource): array | ||||
|     { | ||||
|         $clean_row = []; | ||||
|         foreach (array_values($this->input['report_keys']) as $key => $value) { | ||||
|          | ||||
|             $report_keys = explode(".", $value); | ||||
|              | ||||
|             $column_key = $value; | ||||
|             $clean_row[$key]['entity'] = $report_keys[0]; | ||||
|             $clean_row[$key]['id'] = $report_keys[1] ?? $report_keys[0]; | ||||
|             $clean_row[$key]['hashed_id'] = $report_keys[0] == 'client' ? null : $resource->{$report_keys[0]}->hashed_id ?? null; | ||||
|             $clean_row[$key]['value'] = $row[$column_key]; | ||||
|             $clean_row[$key]['identifier'] = $key; | ||||
| 
 | ||||
|             if(in_array($clean_row[$key]['id'], ['paid_to_date', 'balance', 'credit_balance','payment_balance'])) | ||||
|                 $clean_row[$key]['display_value'] = Number::formatMoney($row[$column_key], $resource); | ||||
|             else | ||||
|                 $clean_row[$key]['display_value'] = $row[$column_key]; | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         return $clean_row; | ||||
|     } | ||||
| 
 | ||||
|     private function decorateAdvancedFields(Client $client, array $entity) :array | ||||
|     { | ||||
|         if (in_array('client.user', $this->input['report_keys'])) { | ||||
|  | ||||
| @ -94,7 +94,8 @@ class ContactExport extends BaseExport | ||||
| 
 | ||||
|         $report = $query->cursor() | ||||
|                 ->map(function ($contact) { | ||||
|                     return $this->buildRow($contact); | ||||
|                     $row = $this->buildRow($contact); | ||||
|                     return $this->processMetaData($row, $contact); | ||||
|                 })->toArray(); | ||||
|          | ||||
|         return array_merge(['columns' => $header], $report); | ||||
|  | ||||
| @ -19,7 +19,7 @@ use App\Models\Company; | ||||
| use App\Libraries\MultiDB; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use App\Transformers\CreditTransformer; | ||||
| use Illuminate\Contracts\Database\Eloquent\Builder; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| 
 | ||||
| class CreditExport extends BaseExport | ||||
| { | ||||
| @ -56,7 +56,7 @@ class CreditExport extends BaseExport | ||||
|         return array_merge(['columns' => $header], $report); | ||||
|     } | ||||
| 
 | ||||
|     private function processMetaData(array $row, Credit $credit): array | ||||
|     public function processMetaData(array $row, $resource): array | ||||
|     { | ||||
|         $clean_row = []; | ||||
|         foreach (array_values($this->input['report_keys']) as $key => $value) { | ||||
| @ -66,12 +66,12 @@ class CreditExport extends BaseExport | ||||
|             $column_key = $value; | ||||
|             $clean_row[$key]['entity'] = $report_keys[0]; | ||||
|             $clean_row[$key]['id'] = $report_keys[1] ?? $report_keys[0]; | ||||
|             $clean_row[$key]['hashed_id'] = $report_keys[0] == 'credit' ? null : $credit->{$report_keys[0]}->hashed_id ?? null; | ||||
|             $clean_row[$key]['hashed_id'] = $report_keys[0] == 'credit' ? null : $resource->{$report_keys[0]}->hashed_id ?? null; | ||||
|             $clean_row[$key]['value'] = $row[$column_key]; | ||||
|             $clean_row[$key]['identifier'] = $value; | ||||
| 
 | ||||
|             if(in_array($clean_row[$key]['id'], ['paid_to_date','total_taxes','amount', 'balance', 'partial', 'refunded', 'applied','unit_cost','cost','price'])) | ||||
|                 $clean_row[$key]['display_value'] = Number::formatMoney($row[$column_key], $credit->client); | ||||
|                 $clean_row[$key]['display_value'] = Number::formatMoney($row[$column_key], $resource->client); | ||||
|             else | ||||
|                 $clean_row[$key]['display_value'] = $row[$column_key]; | ||||
| 
 | ||||
|  | ||||
| @ -16,7 +16,7 @@ use App\Models\Company; | ||||
| use App\Models\Document; | ||||
| use App\Transformers\DocumentTransformer; | ||||
| use App\Utils\Ninja; | ||||
| use Illuminate\Contracts\Database\Eloquent\Builder; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use League\Csv\Writer; | ||||
| 
 | ||||
| @ -56,6 +56,7 @@ class DocumentExport extends BaseExport | ||||
|         $report = $query->cursor() | ||||
|                 ->map(function ($document) { | ||||
|                     $row = $this->buildRow($document); | ||||
|                     return $this->processMetaData($row, $document); | ||||
|                 })->toArray(); | ||||
|          | ||||
|         return array_merge(['columns' => $header], $report); | ||||
|  | ||||
| @ -49,7 +49,8 @@ class ExpenseExport extends BaseExport | ||||
| 
 | ||||
|         $report = $query->cursor() | ||||
|                 ->map(function ($resource) { | ||||
|                     return $this->buildRow($resource); | ||||
|                     $row = $this->buildRow($resource); | ||||
|                     return $this->processMetaData($row, $resource); | ||||
|                 })->toArray(); | ||||
|          | ||||
|         return array_merge(['columns' => $header], $report); | ||||
| @ -158,6 +159,10 @@ class ExpenseExport extends BaseExport | ||||
|             $entity['expense.assigned_user'] = $expense->assigned_user ? $expense->assigned_user->present()->name() : ''; | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('expense.category_id', $this->input['report_keys'])) { | ||||
|             $entity['expense.category_id'] = $expense->category ? $expense->category->name : ''; | ||||
|         } | ||||
| 
 | ||||
|         return $entity; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -20,7 +20,7 @@ use App\Libraries\MultiDB; | ||||
| use App\Export\CSV\BaseExport; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use App\Transformers\InvoiceTransformer; | ||||
| use Illuminate\Contracts\Database\Eloquent\Builder; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| 
 | ||||
| class InvoiceExport extends BaseExport | ||||
| { | ||||
| @ -78,7 +78,8 @@ class InvoiceExport extends BaseExport | ||||
| 
 | ||||
|         $report = $query->cursor() | ||||
|                 ->map(function ($resource) { | ||||
|                     return $this->buildRow($resource); | ||||
|                     $row = $this->buildRow($resource); | ||||
|                     return $this->processMetaData($row, $resource); | ||||
|                 })->toArray(); | ||||
|          | ||||
|         return array_merge(['columns' => $header], $report); | ||||
| @ -94,7 +95,6 @@ class InvoiceExport extends BaseExport | ||||
|         //insert the header
 | ||||
|         $this->csv->insertOne($this->buildHeader()); | ||||
| 
 | ||||
| 
 | ||||
|         $query->cursor() | ||||
|             ->each(function ($invoice) { | ||||
|                 $this->csv->insertOne($this->buildRow($invoice)); | ||||
|  | ||||
| @ -16,7 +16,7 @@ use App\Models\Company; | ||||
| use App\Models\Invoice; | ||||
| use App\Transformers\InvoiceTransformer; | ||||
| use App\Utils\Ninja; | ||||
| use Illuminate\Contracts\Database\Eloquent\Builder; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use League\Csv\Writer; | ||||
| 
 | ||||
| @ -33,6 +33,8 @@ class InvoiceItemExport extends BaseExport | ||||
| 
 | ||||
|     private array $storage_array = []; | ||||
| 
 | ||||
|     private array $storage_item_array = []; | ||||
| 
 | ||||
|     private array $decorate_keys = [ | ||||
|         'client', | ||||
|         'currency_id', | ||||
| @ -62,7 +64,8 @@ class InvoiceItemExport extends BaseExport | ||||
| 
 | ||||
|         $query = Invoice::query() | ||||
|                         ->withTrashed() | ||||
|                         ->with('client')->where('company_id', $this->company->id) | ||||
|                         ->with('client') | ||||
|                         ->where('company_id', $this->company->id) | ||||
|                         ->where('is_deleted', 0); | ||||
| 
 | ||||
|         $query = $this->addDateRange($query); | ||||
| @ -81,12 +84,21 @@ class InvoiceItemExport extends BaseExport | ||||
|                 return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; | ||||
|             })->toArray(); | ||||
| 
 | ||||
| 
 | ||||
|         $query->cursor() | ||||
|               ->each(function ($resource) { | ||||
|             ->each(function ($resource) { | ||||
|                 $this->iterateItems($resource); | ||||
|                }); | ||||
|          | ||||
|         return array_merge(['columns' => $header], $this->storage_array); | ||||
|                          | ||||
|                 foreach($this->storage_array as $row) { | ||||
|                     $this->storage_item_array[] = $this->processItemMetaData($row, $resource); | ||||
|                 } | ||||
| 
 | ||||
|                 $this->storage_array = []; | ||||
|                          | ||||
|             }); | ||||
|                  | ||||
|         return array_merge(['columns' => $header], $this->storage_item_array); | ||||
|                 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| @ -188,13 +200,7 @@ class InvoiceItemExport extends BaseExport | ||||
|             $entity['tax_category'] = $invoice->taxTypeString($entity['tax_category']); | ||||
|         } | ||||
| 
 | ||||
|         // if($this->force_keys) {
 | ||||
|         //     $entity['client'] = $invoice->client->present()->name();
 | ||||
|         //     $entity['client_id_number'] = $invoice->client->id_number;
 | ||||
|         //     $entity['client_number'] = $invoice->client->number;
 | ||||
|         //     $entity['status'] = $invoice->stringStatus($invoice->status_id);
 | ||||
|         // }
 | ||||
| 
 | ||||
|         return $entity; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -16,7 +16,7 @@ use App\Models\Company; | ||||
| use App\Models\Payment; | ||||
| use App\Transformers\PaymentTransformer; | ||||
| use App\Utils\Ninja; | ||||
| use Illuminate\Contracts\Database\Eloquent\Builder; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use League\Csv\Writer; | ||||
| 
 | ||||
| @ -28,40 +28,6 @@ class PaymentExport extends BaseExport | ||||
| 
 | ||||
|     public Writer $csv; | ||||
| 
 | ||||
|     // public array $entity_keys = [
 | ||||
|     //     'amount' => 'amount',
 | ||||
|     //     'applied' => 'applied',
 | ||||
|     //     'client' => 'client_id',
 | ||||
|     //     'currency' => 'currency_id',
 | ||||
|     //     'custom_value1' => 'custom_value1',
 | ||||
|     //     'custom_value2' => 'custom_value2',
 | ||||
|     //     'custom_value3' => 'custom_value3',
 | ||||
|     //     'custom_value4' => 'custom_value4',
 | ||||
|     //     'date' => 'date',
 | ||||
|     //     'exchange_currency' => 'exchange_currency_id',
 | ||||
|     //     'gateway' => 'gateway_type_id',
 | ||||
|     //     'number' => 'number',
 | ||||
|     //     'private_notes' => 'private_notes',
 | ||||
|     //     'project' => 'project_id',
 | ||||
|     //     'refunded' => 'refunded',
 | ||||
|     //     'status' => 'status_id',
 | ||||
|     //     'transaction_reference' => 'transaction_reference',
 | ||||
|     //     'type' => 'type_id',
 | ||||
|     //     'vendor' => 'vendor_id',
 | ||||
|     //     'invoices' => 'invoices',
 | ||||
|     // ];
 | ||||
| 
 | ||||
|     // private array $decorate_keys = [
 | ||||
|     //     'vendor',
 | ||||
|     //     'status',
 | ||||
|     //     'project',
 | ||||
|     //     'client',
 | ||||
|     //     'currency',
 | ||||
|     //     'exchange_currency',
 | ||||
|     //     'type',
 | ||||
|     //     'invoices',
 | ||||
|     // ];
 | ||||
| 
 | ||||
|     public function __construct(Company $company, array $input) | ||||
|     { | ||||
|         $this->company = $company; | ||||
| @ -105,12 +71,12 @@ class PaymentExport extends BaseExport | ||||
| 
 | ||||
|         $report = $query->cursor() | ||||
|                 ->map(function ($resource) { | ||||
|                     return $this->buildRow($resource); | ||||
|                     $row = $this->buildRow($resource); | ||||
|                     return $this->processMetaData($row, $resource); | ||||
|                 })->toArray(); | ||||
|                  | ||||
|         return array_merge(['columns' => $header], $report); | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public function run() | ||||
|  | ||||
| @ -13,11 +13,10 @@ namespace App\Export\CSV; | ||||
| 
 | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Models\Company; | ||||
| use App\Models\Document; | ||||
| use App\Models\Product; | ||||
| use App\Transformers\ProductTransformer; | ||||
| use App\Utils\Ninja; | ||||
| use Illuminate\Contracts\Database\Eloquent\Builder; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use League\Csv\Writer; | ||||
| 
 | ||||
| @ -48,7 +47,8 @@ class ProductExport extends BaseExport | ||||
| 
 | ||||
|         $report = $query->cursor() | ||||
|                 ->map(function ($resource) { | ||||
|                     return $this->buildRow($resource); | ||||
|                     $row = $this->buildRow($resource); | ||||
|                     return $this->processMetaData($row, $resource);     | ||||
|                 })->toArray(); | ||||
|          | ||||
|         return array_merge(['columns' => $header], $report); | ||||
|  | ||||
| @ -19,7 +19,7 @@ use App\Libraries\MultiDB; | ||||
| use App\Models\PurchaseOrder; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use App\Transformers\PurchaseOrderTransformer; | ||||
| use Illuminate\Contracts\Database\Eloquent\Builder; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| 
 | ||||
| class PurchaseOrderExport extends BaseExport | ||||
| { | ||||
| @ -119,7 +119,8 @@ class PurchaseOrderExport extends BaseExport | ||||
| 
 | ||||
|         $report = $query->cursor() | ||||
|                 ->map(function ($resource) { | ||||
|                     return $this->buildRow($resource); | ||||
|                     $row = $this->buildRow($resource); | ||||
|                     return $this->processMetaData($row, $resource); | ||||
|                 })->toArray(); | ||||
|          | ||||
|         return array_merge(['columns' => $header], $report); | ||||
|  | ||||
| @ -11,14 +11,14 @@ | ||||
| 
 | ||||
| namespace App\Export\CSV; | ||||
| 
 | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Models\Company; | ||||
| use App\Models\PurchaseOrder; | ||||
| use App\Transformers\PurchaseOrderTransformer; | ||||
| use App\Utils\Ninja; | ||||
| use Illuminate\Contracts\Database\Eloquent\Builder; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use League\Csv\Writer; | ||||
| use App\Models\Company; | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Models\PurchaseOrder; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use App\Transformers\PurchaseOrderTransformer; | ||||
| 
 | ||||
| class PurchaseOrderItemExport extends BaseExport | ||||
| { | ||||
| @ -33,6 +33,8 @@ class PurchaseOrderItemExport extends BaseExport | ||||
| 
 | ||||
|     private array $storage_array = []; | ||||
| 
 | ||||
|     private array $storage_item_array = []; | ||||
| 
 | ||||
|     public function __construct(Company $company, array $input) | ||||
|     { | ||||
|         $this->company = $company; | ||||
| @ -50,7 +52,6 @@ class PurchaseOrderItemExport extends BaseExport | ||||
|         $t->replace(Ninja::transformTranslations($this->company->settings)); | ||||
| 
 | ||||
|         if (count($this->input['report_keys']) == 0) { | ||||
|             // $this->force_keys = true;
 | ||||
|             $this->input['report_keys'] = array_values($this->mergeItemsKeys('purchase_order_report_keys')); | ||||
|         } | ||||
| 
 | ||||
| @ -78,9 +79,16 @@ class PurchaseOrderItemExport extends BaseExport | ||||
|         $query->cursor() | ||||
|               ->each(function ($resource) { | ||||
|                 $this->iterateItems($resource); | ||||
|                  | ||||
|                 foreach($this->storage_array as $row) { | ||||
|                     $this->storage_item_array[] = $this->processItemMetaData($row, $resource); | ||||
|                 } | ||||
| 
 | ||||
|                 $this->storage_array = []; | ||||
|                  | ||||
|                }); | ||||
|          | ||||
|         return array_merge(['columns' => $header], $this->storage_array); | ||||
|         return array_merge(['columns' => $header], $this->storage_item_array); | ||||
|     } | ||||
| 
 | ||||
|     public function run() | ||||
| @ -113,7 +121,7 @@ class PurchaseOrderItemExport extends BaseExport | ||||
|         foreach ($purchase_order->line_items as $item) { | ||||
|             $item_array = []; | ||||
| 
 | ||||
|             foreach (array_values($this->input['report_keys']) as $key) { //items iterator produces item array
 | ||||
|             foreach (array_values(array_intersect($this->input['report_keys'], $this->item_report_keys)) as $key) { //items iterator produces item array
 | ||||
|                  | ||||
|                 if (str_contains($key, "item.")) { | ||||
| 
 | ||||
| @ -190,4 +198,5 @@ class PurchaseOrderItemExport extends BaseExport | ||||
| 
 | ||||
|         return $entity; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -16,7 +16,7 @@ use App\Models\Company; | ||||
| use App\Models\Quote; | ||||
| use App\Transformers\QuoteTransformer; | ||||
| use App\Utils\Ninja; | ||||
| use Illuminate\Contracts\Database\Eloquent\Builder; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use League\Csv\Writer; | ||||
| 
 | ||||
| @ -80,7 +80,8 @@ class QuoteExport extends BaseExport | ||||
| 
 | ||||
|         $report = $query->cursor() | ||||
|                 ->map(function ($resource) { | ||||
|                     return $this->buildRow($resource); | ||||
|                     $row = $this->buildRow($resource); | ||||
|                     return $this->processMetaData($row, $resource); | ||||
|                 })->toArray(); | ||||
|                  | ||||
|         return array_merge(['columns' => $header], $report); | ||||
|  | ||||
| @ -16,7 +16,7 @@ use App\Models\Company; | ||||
| use App\Models\Quote; | ||||
| use App\Transformers\QuoteTransformer; | ||||
| use App\Utils\Ninja; | ||||
| use Illuminate\Contracts\Database\Eloquent\Builder; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use League\Csv\Writer; | ||||
| 
 | ||||
| @ -29,7 +29,8 @@ class QuoteItemExport extends BaseExport | ||||
| 
 | ||||
|     public Writer $csv; | ||||
| 
 | ||||
|     private array $storage_array; | ||||
|     private array $storage_array = []; | ||||
|     private array $storage_item_array = []; | ||||
| 
 | ||||
|     private array $decorate_keys = [ | ||||
|         'client', | ||||
| @ -77,12 +78,20 @@ class QuoteItemExport extends BaseExport | ||||
|                 return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; | ||||
|             })->toArray(); | ||||
| 
 | ||||
|         $query->cursor() | ||||
|               ->each(function ($resource) { | ||||
|                 $this->iterateItems($resource); | ||||
|                }); | ||||
|          | ||||
|         return array_merge(['columns' => $header], $this->storage_array); | ||||
|             $query->cursor() | ||||
|                 ->each(function ($resource) { | ||||
|                     $this->iterateItems($resource); | ||||
|                                      | ||||
|                     foreach($this->storage_array as $row) { | ||||
|                         $this->storage_item_array[] = $this->processItemMetaData($row, $resource); | ||||
|                     } | ||||
| 
 | ||||
|                     $this->storage_array = []; | ||||
|                                      | ||||
|                 }); | ||||
|                              | ||||
|             return array_merge(['columns' => $header], $this->storage_item_array); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -16,6 +16,7 @@ use App\Models\Company; | ||||
| use App\Models\RecurringInvoice; | ||||
| use App\Transformers\RecurringInvoiceTransformer; | ||||
| use App\Utils\Ninja; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use League\Csv\Writer; | ||||
| 
 | ||||
| @ -28,55 +29,6 @@ class RecurringInvoiceExport extends BaseExport | ||||
| 
 | ||||
|     public Writer $csv; | ||||
| 
 | ||||
|     public array $entity_keys = [ | ||||
|         'amount' => 'amount', | ||||
|         'balance' => 'balance', | ||||
|         'client' => 'client_id', | ||||
|         // 'custom_surcharge1' => 'custom_surcharge1',
 | ||||
|         // 'custom_surcharge2' => 'custom_surcharge2',
 | ||||
|         // 'custom_surcharge3' => 'custom_surcharge3',
 | ||||
|         // 'custom_surcharge4' => 'custom_surcharge4',
 | ||||
|         'custom_value1' => 'custom_value1', | ||||
|         'custom_value2' => 'custom_value2', | ||||
|         'custom_value3' => 'custom_value3', | ||||
|         'custom_value4' => 'custom_value4', | ||||
|         'date' => 'date', | ||||
|         'discount' => 'discount', | ||||
|         'due_date' => 'due_date', | ||||
|         'exchange_rate' => 'exchange_rate', | ||||
|         'footer' => 'footer', | ||||
|         'number' => 'number', | ||||
|         'paid_to_date' => 'paid_to_date', | ||||
|         'partial' => 'partial', | ||||
|         'partial_due_date' => 'partial_due_date', | ||||
|         'po_number' => 'po_number', | ||||
|         'private_notes' => 'private_notes', | ||||
|         'public_notes' => 'public_notes', | ||||
|         'next_send_date' => 'next_send_date', | ||||
|         'status' => 'status_id', | ||||
|         'tax_name1' => 'tax_name1', | ||||
|         'tax_name2' => 'tax_name2', | ||||
|         'tax_name3' => 'tax_name3', | ||||
|         'tax_rate1' => 'tax_rate1', | ||||
|         'tax_rate2' => 'tax_rate2', | ||||
|         'tax_rate3' => 'tax_rate3', | ||||
|         'terms' => 'terms', | ||||
|         'total_taxes' => 'total_taxes', | ||||
|         'currency' => 'currency_id', | ||||
|         'vendor' => 'vendor_id', | ||||
|         'project' => 'project_id', | ||||
|         'frequency_id' => 'frequency_id', | ||||
|     ]; | ||||
| 
 | ||||
|     private array $decorate_keys = [ | ||||
|         'country', | ||||
|         'client', | ||||
|         'currency', | ||||
|         'status', | ||||
|         'vendor', | ||||
|         'project', | ||||
|     ]; | ||||
| 
 | ||||
|     public function __construct(Company $company, array $input) | ||||
|     { | ||||
|         $this->company = $company; | ||||
| @ -84,7 +36,7 @@ class RecurringInvoiceExport extends BaseExport | ||||
|         $this->invoice_transformer = new RecurringInvoiceTransformer(); | ||||
|     } | ||||
| 
 | ||||
|     public function run() | ||||
|     public function init(): Builder | ||||
|     { | ||||
|         MultiDB::setDb($this->company->db); | ||||
|         App::forgetInstance('translator'); | ||||
| @ -92,23 +44,33 @@ class RecurringInvoiceExport extends BaseExport | ||||
|         $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); | ||||
|             $this->input['report_keys'] = array_values($this->recurring_invoice_report_keys); | ||||
|         } | ||||
| 
 | ||||
|         //insert the header
 | ||||
|         $this->csv->insertOne($this->buildHeader()); | ||||
| 
 | ||||
|         $query = RecurringInvoice::query() | ||||
|                         ->withTrashed() | ||||
|                         ->with('client')->where('company_id', $this->company->id) | ||||
|                         ->with('client') | ||||
|                         ->where('company_id', $this->company->id) | ||||
|                         ->where('is_deleted', 0); | ||||
| 
 | ||||
|         $query = $this->addDateRange($query); | ||||
| 
 | ||||
|         return $query; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public function run() | ||||
|     { | ||||
| 
 | ||||
|         $query  = $this->init(); | ||||
| 
 | ||||
|         //load the CSV document from a string
 | ||||
|         $this->csv = Writer::createFromString(); | ||||
| 
 | ||||
|         //insert the header
 | ||||
|         $this->csv->insertOne($this->buildHeader()); | ||||
| 
 | ||||
|         $query->cursor() | ||||
|             ->each(function ($invoice) { | ||||
|                 $this->csv->insertOne($this->buildRow($invoice)); | ||||
| @ -117,6 +79,27 @@ class RecurringInvoiceExport extends BaseExport | ||||
|         return $this->csv->toString(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public function returnJson() | ||||
|     { | ||||
|         $query = $this->init(); | ||||
| 
 | ||||
|         $headerdisplay = $this->buildHeader(); | ||||
| 
 | ||||
|         $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ | ||||
|                 return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; | ||||
|             })->toArray(); | ||||
| 
 | ||||
|         $report = $query->cursor() | ||||
|                 ->map(function ($resource) { | ||||
|                     $row = $this->buildRow($resource); | ||||
|                     return $this->processMetaData($row, $resource); | ||||
|                 })->toArray(); | ||||
|          | ||||
|         return array_merge(['columns' => $header], $report); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private function buildRow(RecurringInvoice $invoice) :array | ||||
|     { | ||||
|         $transformed_invoice = $this->invoice_transformer->transform($invoice); | ||||
| @ -124,22 +107,13 @@ class RecurringInvoiceExport extends BaseExport | ||||
|         $entity = []; | ||||
| 
 | ||||
|         foreach (array_values($this->input['report_keys']) as $key) { | ||||
|             $keyval = array_search($key, $this->entity_keys); | ||||
| 
 | ||||
|             if(!$keyval) { | ||||
|                 $keyval = array_search(str_replace("recurring_invoice.", "", $key), $this->entity_keys) ?? $key; | ||||
|             } | ||||
|             $parts = explode('.', $key); | ||||
| 
 | ||||
|             if(!$keyval) { | ||||
|                 $keyval = $key; | ||||
|             } | ||||
| 
 | ||||
|             if (array_key_exists($key, $transformed_invoice)) { | ||||
|                 $entity[$keyval] = $transformed_invoice[$key]; | ||||
|             } elseif (array_key_exists($keyval, $transformed_invoice)) { | ||||
|                 $entity[$keyval] = $transformed_invoice[$keyval]; | ||||
|             if (is_array($parts) && $parts[0] == 'recurring_invoice' && array_key_exists($parts[1], $transformed_invoice)) { | ||||
|                 $entity[$key] = $transformed_invoice[$parts[1]]; | ||||
|             } else { | ||||
|                 $entity[$keyval] = $this->resolveKey($keyval, $invoice, $this->invoice_transformer); | ||||
|                 $entity[$key] = $this->resolveKey($key, $invoice, $this->invoice_transformer); | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| @ -174,7 +148,7 @@ class RecurringInvoiceExport extends BaseExport | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('recurring_invoice.frequency_id', $this->input['report_keys']) || in_array('frequency_id', $this->input['report_keys'])) { | ||||
|             $entity['frequency_id'] = $invoice->frequencyForKey($invoice->frequency_id); | ||||
|             $entity['recurring_invoice.frequency_id'] = $invoice->frequencyForKey($invoice->frequency_id); | ||||
|         } | ||||
| 
 | ||||
|         return $entity; | ||||
|  | ||||
| @ -18,6 +18,7 @@ use App\Models\Task; | ||||
| use App\Models\Timezone; | ||||
| use App\Transformers\TaskTransformer; | ||||
| use App\Utils\Ninja; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Support\Carbon; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use League\Csv\Writer; | ||||
| @ -33,30 +34,9 @@ class TaskExport extends BaseExport | ||||
| 
 | ||||
|     public Writer $csv; | ||||
| 
 | ||||
|     public array $entity_keys = [ | ||||
|         'start_date' => 'start_date', | ||||
|         'end_date' => 'end_date', | ||||
|         'duration' => 'duration', | ||||
|         'rate' => 'rate', | ||||
|         'number' => 'number', | ||||
|         'description' => 'description', | ||||
|         'custom_value1' => 'custom_value1', | ||||
|         'custom_value2' => 'custom_value2', | ||||
|         'custom_value3' => 'custom_value3', | ||||
|         'custom_value4' => 'custom_value4', | ||||
|         'status' => 'status_id', | ||||
|         'project' => 'project_id', | ||||
|      ]; | ||||
|     private array $storage_array = []; | ||||
| 
 | ||||
|     private array $decorate_keys = [ | ||||
|         'status', | ||||
|         'project', | ||||
|         'client', | ||||
|         'invoice', | ||||
|         'start_date', | ||||
|         'end_date', | ||||
|         'duration', | ||||
|     ]; | ||||
|     private array $storage_item_array = []; | ||||
| 
 | ||||
|     public function __construct(Company $company, array $input) | ||||
|     { | ||||
| @ -65,7 +45,7 @@ class TaskExport extends BaseExport | ||||
|         $this->entity_transformer = new TaskTransformer(); | ||||
|     } | ||||
| 
 | ||||
|     public function run() | ||||
|     public function init(): Builder | ||||
|     { | ||||
|         MultiDB::setDb($this->company->db); | ||||
|         App::forgetInstance('translator'); | ||||
| @ -74,19 +54,12 @@ class TaskExport extends BaseExport | ||||
|         $t->replace(Ninja::transformTranslations($this->company->settings)); | ||||
| 
 | ||||
|         $this->date_format = DateFormat::find($this->company->settings->date_format_id)->format; | ||||
| 
 | ||||
|         //load the CSV document from a string
 | ||||
|         $this->csv = Writer::createFromString(); | ||||
| 
 | ||||
|         ksort($this->entity_keys); | ||||
| 
 | ||||
|         if (count($this->input['report_keys']) == 0) { | ||||
|             $this->input['report_keys'] = array_values($this->entity_keys); | ||||
|             $this->input['report_keys'] = array_values($this->task_report_keys); | ||||
|         } | ||||
| 
 | ||||
|         //insert the header
 | ||||
|         $this->csv->insertOne($this->buildHeader()); | ||||
| 
 | ||||
|         $query = Task::query() | ||||
|                         ->withTrashed() | ||||
|                         ->where('company_id', $this->company->id) | ||||
| @ -94,51 +67,86 @@ class TaskExport extends BaseExport | ||||
| 
 | ||||
|         $query = $this->addDateRange($query); | ||||
| 
 | ||||
|         return $query; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public function run() | ||||
|     { | ||||
| 
 | ||||
|         $query = $this->init(); | ||||
| 
 | ||||
|         //load the CSV document from a string
 | ||||
|         $this->csv = Writer::createFromString(); | ||||
| 
 | ||||
|         //insert the header
 | ||||
|         $this->csv->insertOne($this->buildHeader()); | ||||
| 
 | ||||
|         $query->cursor() | ||||
|               ->each(function ($entity) { | ||||
|                   $this->buildRow($entity); | ||||
|               }); | ||||
| 
 | ||||
|         $this->csv->insertAll($this->storage_array); | ||||
| 
 | ||||
|         return $this->csv->toString(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public function returnJson() | ||||
|     { | ||||
|         $query = $this->init(); | ||||
| 
 | ||||
|         $headerdisplay = $this->buildHeader(); | ||||
| 
 | ||||
|         $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ | ||||
|                 return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; | ||||
|             })->toArray(); | ||||
| 
 | ||||
|         $query->cursor() | ||||
|                 ->each(function ($resource) { | ||||
|          | ||||
|                     $this->buildRow($resource); | ||||
|          | ||||
|                     foreach($this->storage_array as $row) | ||||
|                     { | ||||
|                         $this->storage_item_array[] = $this->processMetaData($row, $resource); | ||||
|                     } | ||||
| 
 | ||||
|                     $this->storage_array = []; | ||||
|                 }); | ||||
|         nlog($this->storage_item_array); | ||||
|         return array_merge(['columns' => $header], $this->storage_item_array); | ||||
|     } | ||||
| 
 | ||||
|     private function buildRow(Task $task) | ||||
|     { | ||||
|         $entity = []; | ||||
|         $transformed_entity = $this->entity_transformer->transform($task); | ||||
| 
 | ||||
|         foreach (array_values($this->input['report_keys']) as $key) { | ||||
|             $keyval = array_search($key, $this->entity_keys); | ||||
| 
 | ||||
|             if(!$keyval) { | ||||
|                 $keyval = array_search(str_replace("task.", "", $key), $this->entity_keys) ?? $key; | ||||
|             $parts = explode('.', $key); | ||||
| 
 | ||||
|             if (is_array($parts) && $parts[0] == 'task' && array_key_exists($parts[1], $transformed_entity)) { | ||||
|                 $entity[$key] = $transformed_entity[$parts[1]]; | ||||
|             } elseif (array_key_exists($key, $transformed_entity)) { | ||||
|                 $entity[$key] = $transformed_entity[$key]; | ||||
|             } else { | ||||
|                 $entity[$key] = $this->resolveKey($key, $task, $this->entity_transformer); | ||||
|             } | ||||
| 
 | ||||
|             if(!$keyval) { | ||||
|                 $keyval = $key; | ||||
|             } | ||||
| 
 | ||||
|             if (array_key_exists($key, $transformed_entity)) { | ||||
|                 $entity[$keyval] = $transformed_entity[$key]; | ||||
|             } elseif (array_key_exists($keyval, $transformed_entity)) { | ||||
|                 $entity[$keyval] = $transformed_entity[$keyval]; | ||||
|             } | ||||
|             else { | ||||
|                 $entity[$keyval] = $this->resolveKey($keyval, $task, $this->entity_transformer); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         $entity['start_date'] = ''; | ||||
|         $entity['end_date'] = ''; | ||||
|         $entity['duration'] = ''; | ||||
| 
 | ||||
|         $entity['task.start_date'] = ''; | ||||
|         $entity['task.end_date'] = ''; | ||||
|         $entity['task.duration'] = ''; | ||||
| 
 | ||||
|         if (is_null($task->time_log) || (is_array(json_decode($task->time_log, 1)) && count(json_decode($task->time_log, 1)) == 0)) { | ||||
|             $this->csv->insertOne($entity); | ||||
|             $this->storage_array[] = $entity; | ||||
|         } else { | ||||
|             $this->iterateLogs($task, $entity); | ||||
|         } | ||||
| 
 | ||||
|          | ||||
|     } | ||||
| 
 | ||||
| @ -163,41 +171,42 @@ class TaskExport extends BaseExport | ||||
| 
 | ||||
|         foreach ($logs as $key => $item) { | ||||
|             if (in_array('task.start_date', $this->input['report_keys']) || in_array('start_date', $this->input['report_keys'])) { | ||||
|                 $entity['start_date'] = Carbon::createFromTimeStamp($item[0])->setTimezone($timezone_name)->format($date_format_default); | ||||
|                 $entity['task.start_date'] = Carbon::createFromTimeStamp($item[0])->setTimezone($timezone_name)->format($date_format_default); | ||||
|             } | ||||
| 
 | ||||
|             if ((in_array('task.end_date', $this->input['report_keys']) || in_array('end_date', $this->input['report_keys'])) && $item[1] > 0) { | ||||
|                 $entity['end_date'] = Carbon::createFromTimeStamp($item[1])->setTimezone($timezone_name)->format($date_format_default); | ||||
|                 $entity['task.end_date'] = Carbon::createFromTimeStamp($item[1])->setTimezone($timezone_name)->format($date_format_default); | ||||
|             } | ||||
| 
 | ||||
|             if ((in_array('task.end_date', $this->input['report_keys']) || in_array('end_date', $this->input['report_keys'])) && $item[1] == 0) { | ||||
|                 $entity['end_date'] = ctrans('texts.is_running'); | ||||
|                 $entity['task.end_date'] = ctrans('texts.is_running'); | ||||
|             } | ||||
| 
 | ||||
|             if (in_array('task.duration', $this->input['report_keys']) || in_array('duration', $this->input['report_keys'])) { | ||||
|                 $entity['duration'] = $task->calcDuration(); | ||||
|                 $entity['task.duration'] = $task->calcDuration(); | ||||
|             } | ||||
|              | ||||
|             $entity = $this->decorateAdvancedFields($task, $entity); | ||||
|              | ||||
|             $this->csv->insertOne($entity); | ||||
|             $this->storage_array[] = $entity; | ||||
|              | ||||
|             unset($entity['start_date']); | ||||
|             unset($entity['end_date']); | ||||
|             unset($entity['duration']); | ||||
|             unset($entity['task.start_date']); | ||||
|             unset($entity['task.end_date']); | ||||
|             unset($entity['task.duration']); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private function decorateAdvancedFields(Task $task, array $entity) :array | ||||
|     { | ||||
|         if (in_array('status_id', $this->input['report_keys'])) { | ||||
|             $entity['status'] = $task->status()->exists() ? $task->status->name : ''; | ||||
|         if (in_array('task.status_id', $this->input['report_keys'])) { | ||||
|             $entity['task.status_id'] = $task->status()->exists() ? $task->status->name : ''; | ||||
|         } | ||||
| 
 | ||||
|         if (in_array('project_id', $this->input['report_keys'])) { | ||||
|             $entity['project'] = $task->project()->exists() ? $task->project->name : ''; | ||||
|         if (in_array('task.project_id', $this->input['report_keys'])) { | ||||
|             $entity['task.project_id'] = $task->project()->exists() ? $task->project->name : ''; | ||||
|         } | ||||
| 
 | ||||
|          | ||||
|         return $entity; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -17,6 +17,7 @@ use App\Models\Company; | ||||
| use App\Transformers\VendorContactTransformer; | ||||
| use App\Transformers\VendorTransformer; | ||||
| use App\Utils\Ninja; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Support\Facades\App; | ||||
| use League\Csv\Writer; | ||||
| 
 | ||||
| @ -31,42 +32,6 @@ class VendorExport extends BaseExport | ||||
| 
 | ||||
|     public string $date_key = 'created_at'; | ||||
| 
 | ||||
|     public array $entity_keys = [ | ||||
|         'address1' => 'vendor.address1', | ||||
|         'address2' => 'vendor.address2', | ||||
|         'city' => 'vendor.city', | ||||
|         'country' => 'vendor.country_id', | ||||
|         'custom_value1' => 'vendor.custom_value1', | ||||
|         'custom_value2' => 'vendor.custom_value2', | ||||
|         'custom_value3' => 'vendor.custom_value3', | ||||
|         'custom_value4' => 'vendor.custom_value4', | ||||
|         'id_number' => 'vendor.id_number', | ||||
|         'name' => 'vendor.name', | ||||
|         'number' => 'vendor.number', | ||||
|         'phone' => 'vendor.phone', | ||||
|         'postal_code' => 'vendor.postal_code', | ||||
|         'private_notes' => 'vendor.private_notes', | ||||
|         'public_notes' => 'vendor.public_notes', | ||||
|         'state' => 'vendor.state', | ||||
|         'vat_number' => 'vendor.vat_number', | ||||
|         'website' => 'vendor.website', | ||||
|         'currency' => 'vendor.currency', | ||||
|         'first_name' => 'vendor_contact.first_name', | ||||
|         'last_name' => 'vendor_contact.last_name', | ||||
|         'contact_phone' => 'vendor_contact.phone', | ||||
|         'contact_custom_value1' => 'vendor_contact.custom_value1', | ||||
|         'contact_custom_value2' => 'vendor_contact.custom_value2', | ||||
|         'contact_custom_value3' => 'vendor_contact.custom_value3', | ||||
|         'contact_custom_value4' => 'vendor_contact.custom_value4', | ||||
|         'email' => 'vendor_contact.email', | ||||
|         'status' => 'vendor.status' | ||||
|     ]; | ||||
| 
 | ||||
|     private array $decorate_keys = [ | ||||
|         'vendor.country_id', | ||||
|         'vendor.currency', | ||||
|     ]; | ||||
| 
 | ||||
|     public function __construct(Company $company, array $input) | ||||
|     { | ||||
|         $this->company = $company; | ||||
| @ -75,8 +40,9 @@ class VendorExport extends BaseExport | ||||
|         $this->contact_transformer = new VendorContactTransformer(); | ||||
|     } | ||||
| 
 | ||||
|     public function run() | ||||
|     public function init(): Builder | ||||
|     { | ||||
| 
 | ||||
|         MultiDB::setDb($this->company->db); | ||||
|         App::forgetInstance('translator'); | ||||
|         App::setLocale($this->company->locale()); | ||||
| @ -87,19 +53,47 @@ class VendorExport extends BaseExport | ||||
|         $this->csv = Writer::createFromString(); | ||||
| 
 | ||||
|         if (count($this->input['report_keys']) == 0) { | ||||
|             $this->input['report_keys'] = array_values($this->entity_keys); | ||||
|             $this->input['report_keys'] = array_values($this->vendor_report_keys); | ||||
|         } | ||||
|          | ||||
|         //insert the header
 | ||||
|         $this->csv->insertOne($this->buildHeader()); | ||||
| 
 | ||||
|         $query = Vendor::query()->with('contacts') | ||||
|                                 ->withTrashed() | ||||
|                                 ->where('company_id', $this->company->id) | ||||
|                                 ->where('is_deleted', 0); | ||||
|                         ->withTrashed() | ||||
|                         ->where('company_id', $this->company->id) | ||||
|                         ->where('is_deleted', 0); | ||||
| 
 | ||||
|         $query = $this->addDateRange($query); | ||||
| 
 | ||||
|         return $query; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public function returnJson() | ||||
|     { | ||||
|         $query = $this->init(); | ||||
| 
 | ||||
|         $headerdisplay = $this->buildHeader(); | ||||
| 
 | ||||
|         $header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){ | ||||
|                 return ['identifier' => $value, 'display_value' => $headerdisplay[$value]]; | ||||
|             })->toArray(); | ||||
| 
 | ||||
|         $report = $query->cursor() | ||||
|                 ->map(function ($resource) { | ||||
|                     $row = $this->buildRow($resource); | ||||
|                     return $this->processMetaData($row, $resource); | ||||
|                 })->toArray(); | ||||
|          | ||||
|         return array_merge(['columns' => $header], $report); | ||||
|     } | ||||
| 
 | ||||
|     public function run() | ||||
|     { | ||||
|      | ||||
|         $query = $this->init(); | ||||
| 
 | ||||
|         //insert the header
 | ||||
|         $this->csv->insertOne($this->buildHeader()); | ||||
| 
 | ||||
|         $query->cursor() | ||||
|               ->each(function ($vendor) { | ||||
|                   $this->csv->insertOne($this->buildRow($vendor)); | ||||
| @ -110,7 +104,7 @@ class VendorExport extends BaseExport | ||||
| 
 | ||||
|     private function buildRow(Vendor $vendor) :array | ||||
|     { | ||||
|         $transformed_contact = []; | ||||
|         $transformed_contact = false; | ||||
| 
 | ||||
|         $transformed_vendor = $this->vendor_transformer->transform($vendor); | ||||
| 
 | ||||
| @ -123,14 +117,12 @@ class VendorExport extends BaseExport | ||||
|         foreach (array_values($this->input['report_keys']) as $key) { | ||||
|             $parts = explode('.', $key); | ||||
| 
 | ||||
|             $keyval = array_search($key, $this->entity_keys); | ||||
| 
 | ||||
|             if (is_array($parts) && $parts[0] == 'vendor' && array_key_exists($parts[1], $transformed_vendor)) { | ||||
|                 $entity[$keyval] = $transformed_vendor[$parts[1]]; | ||||
|             } elseif (is_array($parts) && $parts[0] == 'vendor_contact' && array_key_exists($parts[1], $transformed_contact)) { | ||||
|                 $entity[$keyval] = $transformed_contact[$parts[1]]; | ||||
|                 $entity[$key] = $transformed_vendor[$parts[1]]; | ||||
|             } elseif (is_array($parts) && $parts[0] == 'vendor_contact' && isset($transformed_contact[$parts[1]])) { | ||||
|                 $entity[$key] = $transformed_contact[$parts[1]]; | ||||
|             } else { | ||||
|                 $entity[$keyval] = ''; | ||||
|                 $entity[$key] = $this->resolveKey($key, $vendor, $this->vendor_transformer); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -32,7 +32,8 @@ class ClientFactory | ||||
|         $client->is_deleted = 0; | ||||
|         $client->client_hash = Str::random(40); | ||||
|         $client->settings = ClientSettings::defaults(); | ||||
| 
 | ||||
|         $client->classification = ''; | ||||
|          | ||||
|         return $client; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -34,7 +34,9 @@ class RecurringExpenseFactory | ||||
|         $recurring_expense->tax_amount1 = 0; | ||||
|         $recurring_expense->tax_amount2 = 0; | ||||
|         $recurring_expense->tax_amount3 = 0; | ||||
|         $recurring_expense->date = null; | ||||
|         $recurring_expense->date = now()->format('Y-m-d'); | ||||
|         $recurring_expense->next_send_date = now()->format('Y-m-d'); | ||||
|         $recurring_expense->next_send_date_client = now()->format('Y-m-d'); | ||||
|         $recurring_expense->payment_date = null; | ||||
|         $recurring_expense->amount = 0; | ||||
|         $recurring_expense->foreign_amount = 0; | ||||
| @ -47,6 +49,7 @@ class RecurringExpenseFactory | ||||
|         $recurring_expense->custom_value4 = ''; | ||||
|         $recurring_expense->uses_inclusive_taxes = true; | ||||
|         $recurring_expense->calculate_tax_by_amount = true; | ||||
|         $recurring_expense->remaining_cycles = -1; | ||||
| 
 | ||||
|         return $recurring_expense; | ||||
|     } | ||||
|  | ||||
| @ -28,6 +28,7 @@ class VendorFactory | ||||
|         $vendor->country_id = 4; | ||||
|         $vendor->is_deleted = 0; | ||||
|         $vendor->vendor_hash = Str::random(40); | ||||
|         $vendor->classification = ''; | ||||
| 
 | ||||
|         return $vendor; | ||||
|     } | ||||
|  | ||||
| @ -38,7 +38,10 @@ class ExpenseFilters extends QueryFilters | ||||
|                 ->orWhere('custom_value1', 'like', '%'.$filter.'%') | ||||
|                 ->orWhere('custom_value2', 'like', '%'.$filter.'%') | ||||
|                 ->orWhere('custom_value3', 'like', '%'.$filter.'%') | ||||
|                 ->orWhere('custom_value4', 'like', '%'.$filter.'%'); | ||||
|                 ->orWhere('custom_value4', 'like', '%'.$filter.'%') | ||||
|                 ->orWhereHas('category', function ($q) use ($filter) { | ||||
|                               $q->where('name', 'like', '%'.$filter.'%'); | ||||
|                           }); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| @ -166,16 +169,27 @@ class ExpenseFilters extends QueryFilters | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         if ($sort_col[0] == 'client_id') { | ||||
|             return $this->builder->orderBy(\App\Models\Client::select('name') | ||||
|         if ($sort_col[0] == 'client_id' && in_array($sort_col[1], ['asc', 'desc'])) { | ||||
|             return $this->builder | ||||
|                     ->orderByRaw('ISNULL(client_id), client_id '. $sort_col[1]) | ||||
|                     ->orderBy(\App\Models\Client::select('name') | ||||
|                     ->whereColumn('clients.id', 'expenses.client_id'), $sort_col[1]); | ||||
|         } | ||||
| 
 | ||||
|         if ($sort_col[0] == 'vendor_id') { | ||||
|             return $this->builder->orderBy(\App\Models\Vendor::select('name') | ||||
|         if ($sort_col[0] == 'vendor_id' && in_array($sort_col[1], ['asc', 'desc'])) { | ||||
|             return $this->builder | ||||
|                     ->orderByRaw('ISNULL(vendor_id), vendor_id '. $sort_col[1]) | ||||
|                     ->orderBy(\App\Models\Vendor::select('name') | ||||
|                     ->whereColumn('vendors.id', 'expenses.vendor_id'), $sort_col[1]); | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         if ($sort_col[0] == 'category_id' && in_array($sort_col[1], ['asc', 'desc'])) { | ||||
|             return $this->builder | ||||
|                     ->orderByRaw('ISNULL(category_id), category_id '. $sort_col[1]) | ||||
|                     ->orderBy(\App\Models\ExpenseCategory::select('name') | ||||
|                     ->whereColumn('expense_categories.id', 'expenses.category_id'), $sort_col[1]); | ||||
|         } | ||||
| 
 | ||||
|         if (is_array($sort_col) && in_array($sort_col[1], ['asc', 'desc']) && in_array($sort_col[0], ['public_notes', 'date', 'id_number', 'custom_value1', 'custom_value2', 'custom_value3', 'custom_value4'])) { | ||||
|             return $this->builder->orderBy($sort_col[0], $sort_col[1]); | ||||
|  | ||||
| @ -229,6 +229,32 @@ class InvoiceFilters extends QueryFilters | ||||
|         return $this->builder->where('due_date', '>=', $date); | ||||
|     } | ||||
| 
 | ||||
|     public function date_range(string $date_range = ''): Builder | ||||
|     { | ||||
|         $parts = explode(",", $date_range); | ||||
| 
 | ||||
|         if (count($parts) != 3) { | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         if(!in_array($parts[0], ['date','due_date'])) { | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         try{ | ||||
| 
 | ||||
|             $start_date = Carbon::parse($parts[1]); | ||||
|             $end_date = Carbon::parse($parts[2]); | ||||
| 
 | ||||
|             return $this->builder->whereBetween($parts[0], [$start_date, $end_date]); | ||||
|         } | ||||
|          | ||||
|         catch(\Exception $e){ | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         return $this->builder; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sorts the list based on $sort. | ||||
|  | ||||
| @ -12,8 +12,9 @@ | ||||
| namespace App\Filters; | ||||
| 
 | ||||
| use App\Models\Payment; | ||||
| use Illuminate\Contracts\Database\Eloquent\Builder as EloquentBuilder; | ||||
| use Illuminate\Support\Carbon; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Contracts\Database\Eloquent\Builder as EloquentBuilder; | ||||
| 
 | ||||
| /** | ||||
|  * PaymentFilters. | ||||
| @ -177,6 +178,33 @@ class PaymentFilters extends QueryFilters | ||||
|         return $this->builder->orderBy($sort_col[0], $sort_col[1]); | ||||
|     } | ||||
| 
 | ||||
|     public function date_range(string $date_range = ''): Builder | ||||
|     { | ||||
|         $parts = explode(",", $date_range); | ||||
| 
 | ||||
|         if (count($parts) != 3) { | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         if(!in_array($parts[0], ['date'])) { | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         try{ | ||||
| 
 | ||||
|             $start_date = Carbon::parse($parts[1]); | ||||
|             $end_date = Carbon::parse($parts[2]); | ||||
| 
 | ||||
|             return $this->builder->whereBetween($parts[0], [$start_date, $end_date]); | ||||
|         } | ||||
|          | ||||
|         catch(\Exception $e){ | ||||
|             return $this->builder; | ||||
|         } | ||||
| 
 | ||||
|         return $this->builder; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Filters the query by the users company ID. | ||||
|      * | ||||
|  | ||||
| @ -391,16 +391,16 @@ class InvoiceItemSum | ||||
|     { | ||||
|         $this->setGroupedTaxes(collect([])); | ||||
| 
 | ||||
|         $item_tax = 0; | ||||
| 
 | ||||
|         foreach ($this->line_items as $this->item) { | ||||
|         foreach ($this->line_items as $key => $this->item) { | ||||
|             if ($this->item->line_total == 0) { | ||||
|                 continue; | ||||
|             } | ||||
|              | ||||
|             $item_tax = 0; | ||||
| 
 | ||||
|             //$amount = $this->item->line_total - ($this->item->line_total * ($this->invoice->discount / $this->sub_total));
 | ||||
|             $amount = ($this->sub_total > 0) ? $this->item->line_total - ($this->item->line_total * ($this->invoice->discount / $this->sub_total)) : 0; | ||||
| 
 | ||||
|             $amount = ($this->sub_total > 0) ? $this->item->line_total - ($this->invoice->discount * ( $this->item->line_total / $this->sub_total)) : 0; | ||||
|              | ||||
|             $item_tax_rate1_total = $this->calcAmountLineTax($this->item->tax_rate1, $amount); | ||||
| 
 | ||||
|             $item_tax += $item_tax_rate1_total; | ||||
| @ -424,9 +424,19 @@ class InvoiceItemSum | ||||
|             if ($item_tax_rate3_total != 0) { | ||||
|                 $this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total); | ||||
|             } | ||||
| 
 | ||||
|             $this->item->gross_line_total = $this->getLineTotal() + $item_tax; | ||||
|             $this->item->tax_amount = $item_tax; | ||||
| 
 | ||||
|             $this->line_items[$key] = $this->item; | ||||
| 
 | ||||
|             $this->setTotalTaxes($this->getTotalTaxes() + $item_tax); | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         $this->setTotalTaxes($item_tax); | ||||
|          | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -349,14 +349,16 @@ class InvoiceItemSumInclusive | ||||
|     { | ||||
|         $this->setGroupedTaxes(collect([])); | ||||
| 
 | ||||
|         $item_tax = 0; | ||||
| 
 | ||||
|         foreach ($this->line_items as $this->item) { | ||||
|             if ($this->sub_total == 0) { | ||||
|                 $amount = $this->item->line_total; | ||||
|             } else { | ||||
|                 $amount = $this->item->line_total - ($this->item->line_total * ($this->invoice->discount / $this->sub_total)); | ||||
|                 $amount = ($this->sub_total > 0) ? $this->item->line_total - ($this->invoice->discount * ($this->item->line_total / $this->sub_total)) : 0; | ||||
|                 // $amount = $this->item->line_total - ($this->item->line_total * ($this->invoice->discount / $this->sub_total));
 | ||||
|             } | ||||
|              | ||||
|             $item_tax = 0; | ||||
| 
 | ||||
|             $item_tax_rate1_total = $this->calcInclusiveLineTax($this->item->tax_rate1, $amount); | ||||
| 
 | ||||
| @ -381,9 +383,17 @@ class InvoiceItemSumInclusive | ||||
|             if ($item_tax_rate3_total != 0) { | ||||
|                 $this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total); | ||||
|             } | ||||
| 
 | ||||
|             $this->setTotalTaxes($this->getTotalTaxes() + $item_tax); | ||||
|             $this->item->gross_line_total = $this->getLineTotal(); | ||||
|              | ||||
|             $this->item->tax_amount = $item_tax; | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         $this->setTotalTaxes($item_tax); | ||||
|         return $this; | ||||
| 
 | ||||
|         // $this->setTotalTaxes($item_tax);
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -308,8 +308,9 @@ class InvoiceSum | ||||
| 
 | ||||
|     public function setTaxMap(): self | ||||
|     { | ||||
|         if ($this->invoice->is_amount_discount == true) { | ||||
|         if ($this->invoice->is_amount_discount) { | ||||
|             $this->invoice_items->calcTaxesWithAmountDiscount(); | ||||
|             $this->invoice->line_items = $this->invoice_items->getLineItems(); | ||||
|         } | ||||
| 
 | ||||
|         $this->tax_map = collect(); | ||||
| @ -327,8 +328,6 @@ class InvoiceSum | ||||
|                 return $value['key'] == $key; | ||||
|             })->sum('total'); | ||||
| 
 | ||||
|             //$total_line_tax -= $this->discount($total_line_tax);
 | ||||
| 
 | ||||
|             $this->tax_map[] = ['name' => $tax_name, 'total' => $total_line_tax]; | ||||
| 
 | ||||
|             $this->total_taxes += $total_line_tax; | ||||
| @ -377,16 +376,6 @@ class InvoiceSum | ||||
| 
 | ||||
|     public function purgeTaxes(): self | ||||
|     { | ||||
|         // $this->tax_rate1 = 0;
 | ||||
|         // $this->tax_name1 = '';
 | ||||
| 
 | ||||
|         // $this->tax_rate2 = 0;
 | ||||
|         // $this->tax_name2 = '';
 | ||||
| 
 | ||||
|         // $this->tax_rate3 = 0;
 | ||||
|         // $this->tax_name3 = '';
 | ||||
| 
 | ||||
|         // $this->discount = 0;
 | ||||
| 
 | ||||
|         $line_items = collect($this->invoice->line_items); | ||||
| 
 | ||||
|  | ||||
| @ -315,8 +315,9 @@ class InvoiceSumInclusive | ||||
| 
 | ||||
|     public function setTaxMap() | ||||
|     { | ||||
|         if ($this->invoice->is_amount_discount == true) { | ||||
|         if ($this->invoice->is_amount_discount) { | ||||
|             $this->invoice_items->calcTaxesWithAmountDiscount(); | ||||
|             $this->invoice->line_items = $this->invoice_items->getLineItems(); | ||||
|         } | ||||
| 
 | ||||
|         $this->tax_map = collect(); | ||||
|  | ||||
| @ -12,37 +12,37 @@ | ||||
| 
 | ||||
| namespace App\Http\Controllers\Auth; | ||||
| 
 | ||||
| use Google_Client; | ||||
| use App\Models\User; | ||||
| use App\Utils\Ninja; | ||||
| use App\Models\Account; | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Utils\TruthSource; | ||||
| use Microsoft\Graph\Model; | ||||
| use App\Models\CompanyUser; | ||||
| use App\Models\CompanyToken; | ||||
| use Illuminate\Http\Request; | ||||
| use App\Libraries\OAuth\OAuth; | ||||
| use App\Events\User\UserLoggedIn; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use PragmaRX\Google2FA\Google2FA; | ||||
| use App\Jobs\Account\CreateAccount; | ||||
| use Illuminate\Support\Facades\Auth; | ||||
| use App\Utils\Traits\User\LoginCache; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
| use Turbo124\Beacon\Facades\LightLogs; | ||||
| use App\Http\Controllers\BaseController; | ||||
| use App\Jobs\Company\CreateCompanyToken; | ||||
| use Illuminate\Support\Facades\Response; | ||||
| use Laravel\Socialite\Facades\Socialite; | ||||
| use App\Http\Requests\Login\LoginRequest; | ||||
| use App\Libraries\OAuth\Providers\Google; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use App\DataMapper\Analytics\LoginFailure; | ||||
| use App\DataMapper\Analytics\LoginSuccess; | ||||
| use App\Utils\Traits\UserSessionAttributes; | ||||
| use App\Events\User\UserLoggedIn; | ||||
| use App\Http\Controllers\BaseController; | ||||
| use App\Http\Requests\Login\LoginRequest; | ||||
| use App\Jobs\Account\CreateAccount; | ||||
| use App\Jobs\Company\CreateCompanyToken; | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Libraries\OAuth\OAuth; | ||||
| use App\Libraries\OAuth\Providers\Google; | ||||
| use App\Models\Account; | ||||
| use App\Models\CompanyToken; | ||||
| use App\Models\CompanyUser; | ||||
| use App\Models\User; | ||||
| use App\Transformers\CompanyUserTransformer; | ||||
| use App\Utils\Ninja; | ||||
| use App\Utils\Traits\User\LoginCache; | ||||
| use App\Utils\Traits\UserSessionAttributes; | ||||
| use App\Utils\TruthSource; | ||||
| use Google_Client; | ||||
| use Illuminate\Database\Eloquent\Builder; | ||||
| use Illuminate\Foundation\Auth\AuthenticatesUsers; | ||||
| use Illuminate\Http\JsonResponse; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Facades\Auth; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
| use Illuminate\Support\Facades\Response; | ||||
| use Laravel\Socialite\Facades\Socialite; | ||||
| use Microsoft\Graph\Model; | ||||
| use PragmaRX\Google2FA\Google2FA; | ||||
| use Turbo124\Beacon\Facades\LightLogs; | ||||
| 
 | ||||
| class LoginController extends BaseController | ||||
| { | ||||
| @ -418,10 +418,12 @@ class LoginController extends BaseController | ||||
|             ->setReturnType(Model\User::class) | ||||
|             ->execute(); | ||||
| 
 | ||||
|         nlog($user); | ||||
| 
 | ||||
|         if ($user) { | ||||
|             $account = request()->input('account'); | ||||
| 
 | ||||
|             $email = $user->getMail() ?: $user->getUserPrincipalName(); | ||||
|             $email = $user->getUserPrincipalName() ?? false; | ||||
| 
 | ||||
|             $query = [ | ||||
|                 'oauth_user_id' => $user->getId(), | ||||
| @ -436,8 +438,8 @@ class LoginController extends BaseController | ||||
|                 return $this->existingOauthUser($existing_user); | ||||
|             } | ||||
| 
 | ||||
|             //If this is a result user/email combo - lets add their OAuth details details
 | ||||
|             if ($existing_login_user = MultiDB::hasUser(['email' => $email])) { | ||||
|             // If this is a result user/email combo - lets add their OAuth details details
 | ||||
|             if ($email && $existing_login_user = MultiDB::hasUser(['email' => $email])) { | ||||
|                 if (!$existing_login_user->account) { | ||||
|                     return response()->json(['message' => 'User exists, but not attached to any companies! Orphaned user!'], 400); | ||||
|                 } | ||||
| @ -447,7 +449,6 @@ class LoginController extends BaseController | ||||
|                 return $this->existingLoginUser($user->getId(), 'microsoft'); | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|             // Signup!
 | ||||
|             if (request()->has('create') && request()->input('create') == 'true') { | ||||
|                 $new_account = [ | ||||
| @ -640,8 +641,9 @@ class LoginController extends BaseController | ||||
|             $parameters = ['response_type' => 'code', 'redirect_uri' => config('ninja.app_url') . "/auth/microsoft"]; | ||||
|         } | ||||
| 
 | ||||
|         if(request()->hasHeader('X-REACT') || request()->query('react')) | ||||
|         if(request()->hasHeader('X-REACT') || request()->query('react')) { | ||||
|             Cache::put("react_redir:".auth()->user()?->account->key, 'true', 300); | ||||
|         } | ||||
| 
 | ||||
|         if (request()->has('code')) { | ||||
|             return $this->handleProviderCallback($provider); | ||||
| @ -698,7 +700,7 @@ class LoginController extends BaseController | ||||
|         $request_from_react = Cache::pull("react_redir:".auth()->user()?->account?->key); | ||||
| 
 | ||||
|         // if($request_from_react)
 | ||||
|             $redirect_url = config('ninja.react_url')."/#/settings/user_details/connect"; | ||||
|         $redirect_url = config('ninja.react_url')."/#/settings/user_details/connect"; | ||||
| 
 | ||||
|         return redirect($redirect_url); | ||||
|     } | ||||
|  | ||||
| @ -12,9 +12,7 @@ | ||||
| namespace App\Http\Controllers; | ||||
| 
 | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use Illuminate\Support\Collection; | ||||
| use App\Models\BankTransactionRule; | ||||
| use App\Filters\BankTransactionFilters; | ||||
| use App\Factory\BankTransactionRuleFactory; | ||||
| use App\Filters\BankTransactionRuleFilters; | ||||
| use App\Repositories\BankTransactionRuleRepository; | ||||
| @ -26,6 +24,7 @@ use App\Http\Requests\BankTransactionRule\StoreBankTransactionRuleRequest; | ||||
| use App\Http\Requests\BankTransactionRule\CreateBankTransactionRuleRequest; | ||||
| use App\Http\Requests\BankTransactionRule\UpdateBankTransactionRuleRequest; | ||||
| use App\Http\Requests\BankTransactionRule\DestroyBankTransactionRuleRequest; | ||||
| use App\Services\Bank\BankMatchingService; | ||||
| 
 | ||||
| class BankTransactionRuleController extends BaseController | ||||
| { | ||||
| @ -256,8 +255,12 @@ class BankTransactionRuleController extends BaseController | ||||
|      */ | ||||
|     public function update(UpdateBankTransactionRuleRequest $request, BankTransactionRule $bank_transaction_rule) | ||||
|     { | ||||
|         //stubs for updating the model
 | ||||
|         $bank_transaction = $this->bank_transaction_repo->save($request->all(), $bank_transaction_rule); | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         $bank_transaction_rule = $this->bank_transaction_repo->save($request->all(), $bank_transaction_rule); | ||||
| 
 | ||||
|         BankMatchingService::dispatch($user->company()->id, $user->company()->db); | ||||
| 
 | ||||
|         return $this->itemResponse($bank_transaction_rule->fresh()); | ||||
|     } | ||||
| @ -304,6 +307,7 @@ class BankTransactionRuleController extends BaseController | ||||
|     { | ||||
|         /** @var \App\Models\User $user **/ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         $bank_transaction_rule = BankTransactionRuleFactory::create($user->company()->id, $user->id); | ||||
| 
 | ||||
|         return $this->itemResponse($bank_transaction_rule); | ||||
| @ -355,6 +359,8 @@ class BankTransactionRuleController extends BaseController | ||||
| 
 | ||||
|         $bank_transaction_rule = $this->bank_transaction_repo->save($request->all(), BankTransactionRuleFactory::create($user->company()->id, $user->id)); | ||||
| 
 | ||||
|         BankMatchingService::dispatch($user->company()->id, $user->company()->db); | ||||
| 
 | ||||
|         return $this->itemResponse($bank_transaction_rule); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -17,6 +17,10 @@ use Illuminate\Support\Facades\Hash; | ||||
| use Illuminate\Support\Str; | ||||
| use Illuminate\View\View; | ||||
| 
 | ||||
| /** | ||||
|  * EntityViewController | ||||
|  * @deprecated 5.7 ? | ||||
|  */ | ||||
| class EntityViewController extends Controller | ||||
| { | ||||
|     use MakesHash; | ||||
|  | ||||
| @ -89,11 +89,12 @@ class InvoiceController extends Controller | ||||
|         $data = Cache::get($hash); | ||||
|         $invitation = false; | ||||
|          | ||||
|         match($data['entity_type']){ | ||||
|         match($data['entity_type'] ?? false){ | ||||
|             'invoice' => $invitation = InvoiceInvitation::withTrashed()->find($data['invitation_id']), | ||||
|             'quote' => $invitation = QuoteInvitation::withTrashed()->find($data['invitation_id']), | ||||
|             'credit' => $invitation = CreditInvitation::withTrashed()->find($data['invitation_id']), | ||||
|             'recurring_invoice' => $invitation = RecurringInvoiceInvitation::withTrashed()->find($data['invitation_id']), | ||||
|             false => $invitation = false, | ||||
|         }; | ||||
| 
 | ||||
|         if (! $invitation) { | ||||
|  | ||||
| @ -12,21 +12,22 @@ | ||||
| 
 | ||||
| namespace App\Http\Controllers\ClientPortal; | ||||
| 
 | ||||
| use App\Events\Misc\InvitationWasViewed; | ||||
| use App\Utils\Ninja; | ||||
| use App\Models\Quote; | ||||
| use Illuminate\View\View; | ||||
| use Illuminate\Http\Request; | ||||
| use App\Models\QuoteInvitation; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use App\Events\Quote\QuoteWasViewed; | ||||
| use App\Http\Controllers\Controller; | ||||
| use App\Http\Requests\ClientPortal\Quotes\ProcessQuotesInBulkRequest; | ||||
| use App\Jobs\Invoice\InjectSignature; | ||||
| use Illuminate\Contracts\View\Factory; | ||||
| use Illuminate\Support\Facades\Storage; | ||||
| use App\Events\Misc\InvitationWasViewed; | ||||
| use Symfony\Component\HttpFoundation\BinaryFileResponse; | ||||
| use App\Http\Requests\ClientPortal\Quotes\ShowQuoteRequest; | ||||
| use App\Http\Requests\ClientPortal\Quotes\ShowQuotesRequest; | ||||
| use App\Jobs\Invoice\InjectSignature; | ||||
| use App\Models\Quote; | ||||
| use App\Utils\Ninja; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use Illuminate\Contracts\View\Factory; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Facades\Storage; | ||||
| use Illuminate\View\View; | ||||
| use Symfony\Component\HttpFoundation\BinaryFileResponse; | ||||
| use App\Http\Requests\ClientPortal\Quotes\ProcessQuotesInBulkRequest; | ||||
| 
 | ||||
| class QuoteController extends Controller | ||||
| { | ||||
| @ -121,37 +122,38 @@ class QuoteController extends Controller | ||||
|         /** @var \App\Models\ClientContact $client_contact **/ | ||||
|         $client_contact = auth()->user(); | ||||
| 
 | ||||
|         $quotes = Quote::query() | ||||
|             ->whereIn('id', $ids) | ||||
|             ->whereClientId($client_contact->client_id) | ||||
|         $quote_invitations = QuoteInvitation::query() | ||||
|             ->with('quote','company') | ||||
|             ->whereIn('quote_id', $ids) | ||||
|             ->where('client_contact_id', $client_contact->id) | ||||
|             ->withTrashed() | ||||
|             ->get(); | ||||
| 
 | ||||
|         if (! $quotes || $quotes->count() == 0) { | ||||
|         if (! $quote_invitations || $quote_invitations->count() == 0) { | ||||
|             return redirect() | ||||
|                 ->route('client.quotes.index') | ||||
|                 ->with('message', ctrans('texts.no_quotes_available_for_download')); | ||||
|         } | ||||
| 
 | ||||
|         if ($quotes->count() == 1) { | ||||
|             $file = $quotes->first()->service()->getQuotePdf(); | ||||
|             // return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
 | ||||
|         if ($quote_invitations->count() == 1) { | ||||
|             $invitation = $quote_invitations->first(); | ||||
|             $file = (new \App\Jobs\Entity\CreateRawPdf($invitation, $invitation->company->db))->handle(); | ||||
|             return response()->streamDownload(function () use ($file) { | ||||
|                 echo Storage::get($file); | ||||
|             }, basename($file), ['Content-Type' => 'application/pdf']); | ||||
|                 echo $file; | ||||
|             }, $invitation->quote->numberFormatter().".pdf", ['Content-Type' => 'application/pdf']); | ||||
|         } | ||||
| 
 | ||||
|         return $this->buildZip($quotes); | ||||
|         return $this->buildZip($quote_invitations); | ||||
|     } | ||||
| 
 | ||||
|     private function buildZip($quotes) | ||||
|     private function buildZip($quote_invitations) | ||||
|     { | ||||
|         // create new archive
 | ||||
|         $zipFile = new \PhpZip\ZipFile(); | ||||
|         try { | ||||
|             foreach ($quotes as $quote) { | ||||
|                 //add it to the zip
 | ||||
|                 $zipFile->addFromString(basename($quote->pdf_file_path()), file_get_contents($quote->pdf_file_path(null, 'url', true))); | ||||
|             foreach ($quote_invitations as $invitation) { | ||||
|                 $file = (new \App\Jobs\Entity\CreateRawPdf($invitation, $invitation->company->db))->handle(); | ||||
|                 $zipFile->addFromString($invitation->quote->numberFormatter() . '.pdf', $file); | ||||
|             } | ||||
| 
 | ||||
|             $filename = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.quotes')).'.zip'; | ||||
| @ -162,7 +164,6 @@ class QuoteController extends Controller | ||||
| 
 | ||||
|             return response()->download($filepath, $filename)->deleteFileAfterSend(true); | ||||
|         } catch (\PhpZip\Exception\ZipException $e) { | ||||
|             // handle exception
 | ||||
|         } finally { | ||||
|             $zipFile->close(); | ||||
|         } | ||||
|  | ||||
| @ -22,9 +22,10 @@ class SwitchCompanyController extends Controller | ||||
| 
 | ||||
|     public function __invoke(string $contact) | ||||
|     { | ||||
|         $client_contact = ClientContact::where('email', auth()->user()->email) | ||||
|             ->where('id', $this->transformKeys($contact)) | ||||
|             ->first(); | ||||
|         $client_contact = ClientContact::query() | ||||
|                                        ->where('email', auth()->user()->email) | ||||
|                                        ->where('id', $this->transformKeys($contact)) | ||||
|                                        ->firstOrFail(); | ||||
| 
 | ||||
|         auth()->guard('contact')->loginUsingId($client_contact->id, true); | ||||
| 
 | ||||
|  | ||||
| @ -697,4 +697,19 @@ class CompanyController extends BaseController | ||||
| 
 | ||||
|         return $this->itemResponse($company->fresh()); | ||||
|     } | ||||
| 
 | ||||
|     public function logo() | ||||
|     { | ||||
| 
 | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
|         $company = $user->company(); | ||||
|         $logo = strlen($company->settings->company_logo) > 5 ? $company->settings->company_logo : 'https://pdf.invoicing.co/favicon-v2.png'; | ||||
|         $headers = ['Content-Disposition' => 'inline']; | ||||
|       | ||||
|         return response()->streamDownload(function () use ($logo){ | ||||
|             echo @file_get_contents($logo); | ||||
|         }, 'logo.png', $headers); | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -111,10 +111,11 @@ class CompanyUserController extends BaseController | ||||
|      */ | ||||
|     public function update(UpdateCompanyUserRequest $request, User $user) | ||||
|     { | ||||
|         /** @var \App\Models\User $auth_user */ | ||||
|         $auth_user = auth()->user(); | ||||
|         $company = $auth_user->company(); | ||||
| 
 | ||||
|         $company = auth()->user()->company(); | ||||
| 
 | ||||
|         $company_user = CompanyUser::whereUserId($user->id)->whereCompanyId($company->id)->first(); | ||||
|         $company_user = CompanyUser::query()->where('user_id', $user->id)->where('company_id',$company->id)->first(); | ||||
| 
 | ||||
|         if (! $company_user) { | ||||
|             throw new ModelNotFoundException(ctrans('texts.company_user_not_found')); | ||||
| @ -122,11 +123,16 @@ class CompanyUserController extends BaseController | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (auth()->user()->isAdmin()) { | ||||
|         if ($auth_user->isAdmin()) { | ||||
|             $company_user->fill($request->input('company_user')); | ||||
|         } else { | ||||
|             $company_user->settings = $request->input('company_user')['settings']; | ||||
|             $company_user->notifications = $request->input('company_user')['notifications']; | ||||
| 
 | ||||
|             if(isset($request->input('company_user')['react_settings'])) { | ||||
|                 $company_user->react_settings = $request->input('company_user')['react_settings']; | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         $company_user->save(); | ||||
| @ -136,8 +142,11 @@ class CompanyUserController extends BaseController | ||||
| 
 | ||||
|     public function updatePreferences(UpdateCompanyUserPreferencesRequest $request, User $user) | ||||
|     { | ||||
|         /** @var \App\Models\User $auth_user */ | ||||
|         $auth_user = auth()->user(); | ||||
|         $company = $auth_user->company(); | ||||
| 
 | ||||
|         $company = auth()->user()->company(); | ||||
|         $company = $auth_user->company(); | ||||
| 
 | ||||
|         $company_user = CompanyUser::whereUserId($user->id)->whereCompanyId($company->id)->first(); | ||||
| 
 | ||||
|  | ||||
| @ -105,7 +105,7 @@ class ConnectedAccountController extends BaseController | ||||
|                       ->execute(); | ||||
| 
 | ||||
|         if ($user) { | ||||
|             $email = $user->getMail() ?: $user->getUserPrincipalName(); | ||||
|             $email = $user->getUserPrincipalName() ?? false; | ||||
| 
 | ||||
|             nlog("microsoft"); | ||||
|             nlog($email); | ||||
|  | ||||
| @ -385,8 +385,8 @@ class CreditController extends BaseController | ||||
|         $credit = $this->credit_repository->save($request->all(), $credit); | ||||
| 
 | ||||
|         $credit->service() | ||||
|                ->triggeredActions($request) | ||||
|                ->deletePdf(); | ||||
|                ->triggeredActions($request); | ||||
|             //    ->deletePdf();
 | ||||
| 
 | ||||
|         /** @var \App\Models\User $user**/ | ||||
|         $user = auth()->user(); | ||||
| @ -529,20 +529,18 @@ class CreditController extends BaseController | ||||
|         if ($action == 'bulk_download' && $credits->count() > 1) { | ||||
|             $credits->each(function ($credit) use($user){ | ||||
|                 if ($user->cannot('view', $credit)) { | ||||
|                     nlog('access denied'); | ||||
| 
 | ||||
|                     return response()->json(['message' => ctrans('text.access_denied')]); | ||||
|                 } | ||||
|             }); | ||||
| 
 | ||||
|             ZipCredits::dispatch($credits, $credits->first()->company, $user); | ||||
|             ZipCredits::dispatch($credits->pluck('id')->toArray(), $credits->first()->company, $user); | ||||
| 
 | ||||
|             return response()->json(['message' => ctrans('texts.sent_message')], 200); | ||||
|         } | ||||
| 
 | ||||
|         if ($action == 'bulk_print' && $user->can('view', $credits->first())) { | ||||
|             $paths = $credits->map(function ($credit) { | ||||
|                 return $credit->service()->getCreditPdf($credit->invitations->first()); | ||||
|                 return (new \App\Jobs\Entity\CreateRawPdf($credit->invitations->first(), $credit->company->db))->handle(); | ||||
|             }); | ||||
| 
 | ||||
|             $merge = (new PdfMerge($paths->toArray()))->run(); | ||||
| @ -592,11 +590,8 @@ class CreditController extends BaseController | ||||
|                 } | ||||
|                 break; | ||||
|             case 'download': | ||||
|                 // $file = $credit->pdf_file_path();
 | ||||
|                 $file = $credit->service()->getCreditPdf($credit->invitations->first()); | ||||
| 
 | ||||
|                 // return response()->download($file, basename($file), ['Cache-Control:' => 'no-cache'])->deleteFileAfterSend(true);
 | ||||
| 
 | ||||
|                 return response()->streamDownload(function () use ($file) { | ||||
|                     echo Storage::get($file); | ||||
|                 }, basename($file), ['Content-Type' => 'application/pdf']); | ||||
|  | ||||
| @ -34,7 +34,6 @@ class EmailHistoryController extends BaseController | ||||
|                  ->map(function ($system_log) { | ||||
|                      if($system_log->log['history'] ?? false) { | ||||
|                         return $system_log->log['history']; | ||||
|                         //  return json_decode($system_log->log['history'], true);
 | ||||
|                      } | ||||
|                  }); | ||||
| 
 | ||||
| @ -60,7 +59,6 @@ class EmailHistoryController extends BaseController | ||||
|                 ->map(function ($system_log) { | ||||
|                     if($system_log->log['history'] ?? false) { | ||||
|                         return $system_log->log['history']; | ||||
|                         // return json_decode($system_log->log['history'], true);
 | ||||
|                     } | ||||
|                 }); | ||||
| 
 | ||||
|  | ||||
| @ -91,16 +91,83 @@ class ImportController extends Controller | ||||
|             $csv_array = $this->getCsvData($contents); | ||||
| 
 | ||||
|             $class_map = $this->getEntityMap($entityType); | ||||
|              | ||||
|             $hints = $this->setImportHints($entityType, $class_map::importable(), $csv_array[0]); | ||||
| 
 | ||||
|             $data['mappings'][$entityType] = [ | ||||
|                 'available' => $class_map::importable(), | ||||
|                 'headers'   => array_slice($csv_array, 0, 2), | ||||
|                 'hints' => $hints, | ||||
|             ]; | ||||
|         } | ||||
| 
 | ||||
|         return response()->json($data); | ||||
|     } | ||||
| 
 | ||||
|     private function setImportHints($entity_type, $available_keys, $headers): array | ||||
|     { | ||||
|         $hints = []; | ||||
| 
 | ||||
|         $translated_keys = collect($available_keys)->map(function ($value,$key){ | ||||
|              | ||||
|             $parts = explode(".", $value); | ||||
|             $index = $parts[0]; | ||||
|             $label = $parts[1] ?? $parts[0]; | ||||
| 
 | ||||
|             return ['key' => $key, 'index' => ctrans("texts.{$index}"), 'label' => ctrans("texts.{$label}")]; | ||||
| 
 | ||||
|         })->toArray(); | ||||
| 
 | ||||
| 
 | ||||
|         foreach($headers as $key => $value) { | ||||
|              | ||||
|             foreach($translated_keys as $tkey => $tvalue) | ||||
|             { | ||||
|                  | ||||
|                 if($this->testMatch($value, $tvalue['label'])) { | ||||
|                     $hit = $tvalue['key']; | ||||
|                     $hints[$key] = $hit; | ||||
|                     unset($translated_keys[$tkey]); | ||||
|                     break; | ||||
|                 } | ||||
|                 else { | ||||
|                     $hints[$key] = null; | ||||
|                 } | ||||
|               | ||||
|             } | ||||
| 
 | ||||
|             | ||||
|         } | ||||
| 
 | ||||
|         //second pass using the index of the translation here
 | ||||
|         foreach($headers as $key => $value) | ||||
|         { | ||||
|             if(isset($hints[$key])) { | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             foreach($translated_keys as $tkey => $tvalue)  | ||||
|             { | ||||
|                 if($this->testMatch($value, $tvalue['index'])) { | ||||
|                     $hit = $tvalue['key']; | ||||
|                     $hints[$key] = $hit; | ||||
|                     unset($translated_keys[$tkey]); | ||||
|                     break; | ||||
|                 } else { | ||||
|                     $hints[$key] = null; | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|         } | ||||
| 
 | ||||
|         return $hints; | ||||
|     } | ||||
| 
 | ||||
|     private function testMatch($haystack, $needle): bool | ||||
|     {   | ||||
|         return stripos($haystack, $needle) !== false; | ||||
|     } | ||||
| 
 | ||||
|     private function convertEncoding($data) | ||||
|     { | ||||
|          | ||||
| @ -115,6 +182,9 @@ class ImportController extends Controller | ||||
| 
 | ||||
|     public function import(ImportRequest $request) | ||||
|     { | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         $data = $request->all(); | ||||
| 
 | ||||
|         if (empty($data['hash'])) { | ||||
| @ -130,7 +200,7 @@ class ImportController extends Controller | ||||
|         } | ||||
| 
 | ||||
|         unset($data['files']); | ||||
|         CSVIngest::dispatch($data, auth()->user()->company()); | ||||
|         CSVIngest::dispatch($data, $user->company()); | ||||
| 
 | ||||
|         return response()->json(['message' => ctrans('texts.import_started')], 200); | ||||
|     } | ||||
|  | ||||
| @ -415,7 +415,7 @@ class InvoiceController extends BaseController | ||||
| 
 | ||||
|         $invoice->service() | ||||
|                 ->triggeredActions($request) | ||||
|                 ->deletePdf() | ||||
|                 // ->deletePdf()
 | ||||
|                 ->adjustInventory($old_invoice); | ||||
| 
 | ||||
|         event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); | ||||
| @ -527,7 +527,7 @@ class InvoiceController extends BaseController | ||||
| 
 | ||||
|         if ($action == 'bulk_print' && $user->can('view', $invoices->first())) { | ||||
|             $paths = $invoices->map(function ($invoice) { | ||||
|                 return $invoice->service()->getInvoicePdf(); | ||||
|                 return (new \App\Jobs\Entity\CreateRawPdf($invoice->invitations->first(), $invoice->company->db))->handle(); | ||||
|             }); | ||||
| 
 | ||||
|             $merge = (new PdfMerge($paths->toArray()))->run(); | ||||
| @ -700,7 +700,7 @@ class InvoiceController extends BaseController | ||||
|                 } | ||||
|                 break; | ||||
|             case 'cancel': | ||||
|                 $invoice = $invoice->service()->handleCancellation()->deletePdf()->save(); | ||||
|                 $invoice = $invoice->service()->handleCancellation()->save(); | ||||
|                 if (! $bulk) { | ||||
|                     $this->itemResponse($invoice); | ||||
|                 } | ||||
|  | ||||
| @ -74,7 +74,7 @@ class OneTimeTokenController extends BaseController | ||||
|             'user_id' => $user->id, | ||||
|             'company_key'=> $user->company()->company_key, | ||||
|             'context' => $request->input('context'), | ||||
|             'is_react' => $request->has('react') && $request->query('react') == 'true' ? true : false, | ||||
|             'is_react' => $request->hasHeader('X-REACT') ? true : false, | ||||
|         ]; | ||||
| 
 | ||||
|         Cache::put($hash, $data, 3600); | ||||
|  | ||||
| @ -139,7 +139,10 @@ class PurchaseOrderController extends BaseController | ||||
|      */ | ||||
|     public function create(CreatePurchaseOrderRequest $request) | ||||
|     { | ||||
|         $purchase_order = PurchaseOrderFactory::create(auth()->user()->company()->id, auth()->user()->id); | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         $purchase_order = PurchaseOrderFactory::create($user->company()->id, $user->id); | ||||
| 
 | ||||
|         return $this->itemResponse($purchase_order); | ||||
|     } | ||||
| @ -183,7 +186,10 @@ class PurchaseOrderController extends BaseController | ||||
|      */ | ||||
|     public function store(StorePurchaseOrderRequest $request) | ||||
|     { | ||||
|         $purchase_order = $this->purchase_order_repository->save($request->all(), PurchaseOrderFactory::create(auth()->user()->company()->id, auth()->user()->id)); | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         $purchase_order = $this->purchase_order_repository->save($request->all(), PurchaseOrderFactory::create($user->company()->id, $user->id)); | ||||
| 
 | ||||
|         $purchase_order = $purchase_order->service() | ||||
|             ->fillDefaults() | ||||
| @ -361,7 +367,7 @@ class PurchaseOrderController extends BaseController | ||||
| 
 | ||||
|         $purchase_order = $purchase_order->service() | ||||
|             ->triggeredActions($request) | ||||
|             ->touchPdf() | ||||
|             // ->touchPdf()
 | ||||
|             ->save(); | ||||
| 
 | ||||
|         event(new PurchaseOrderWasUpdated($purchase_order, $purchase_order->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); | ||||
| @ -475,11 +481,14 @@ class PurchaseOrderController extends BaseController | ||||
|      */ | ||||
|     public function bulk(BulkPurchaseOrderRequest $request) | ||||
|     { | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         $action = $request->input('action'); | ||||
| 
 | ||||
|         $ids = $request->input('ids'); | ||||
| 
 | ||||
|         if (Ninja::isHosted() && (stripos($action, 'email') !== false) && !auth()->user()->company()->account->account_sms_verified) { | ||||
|         if (Ninja::isHosted() && (stripos($action, 'email') !== false) && !$user->company()->account->account_sms_verified) { | ||||
|             return response(['message' => 'Please verify your account to send emails.'], 400); | ||||
|         } | ||||
| 
 | ||||
| @ -493,20 +502,20 @@ 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)) { | ||||
|             $purchase_orders->each(function ($purchase_order) use ($user){ | ||||
|                 if ($user->cannot('view', $purchase_order)) { | ||||
|                     return response()->json(['message' => ctrans('text.access_denied')]); | ||||
|                 } | ||||
|             }); | ||||
| 
 | ||||
|             ZipPurchaseOrders::dispatch($purchase_orders, $purchase_orders->first()->company, auth()->user()); | ||||
|             ZipPurchaseOrders::dispatch($purchase_orders->pluck("id")->toArray(), $purchase_orders->first()->company, auth()->user()); | ||||
| 
 | ||||
|             return response()->json(['message' => ctrans('texts.sent_message')], 200); | ||||
|         } | ||||
| 
 | ||||
|         if ($action == 'bulk_print' && auth()->user()->can('view', $purchase_orders->first())) { | ||||
|         if ($action == 'bulk_print' && $user->can('view', $purchase_orders->first())) { | ||||
|             $paths = $purchase_orders->map(function ($purchase_order) { | ||||
|                 return $purchase_order->service()->getPurchaseOrderPdf(); | ||||
|                 return (new \App\Jobs\Vendor\CreatePurchaseOrderPdf($purchase_order->invitations->first()))->rawPdf(); | ||||
|             }); | ||||
| 
 | ||||
|             $merge = (new PdfMerge($paths->toArray()))->run(); | ||||
| @ -519,8 +528,8 @@ class PurchaseOrderController extends BaseController | ||||
|         /* | ||||
|          * Send the other actions to the switch | ||||
|          */ | ||||
|         $purchase_orders->each(function ($purchase_order, $key) use ($action) { | ||||
|             if (auth()->user()->can('edit', $purchase_order)) { | ||||
|         $purchase_orders->each(function ($purchase_order, $key) use ($action, $user) { | ||||
|             if ($user->can('edit', $purchase_order)) { | ||||
|                 $this->performAction($purchase_order, $action, true); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
| @ -16,6 +16,7 @@ use App\Models\Quote; | ||||
| use App\Models\Client; | ||||
| use App\Models\Account; | ||||
| use App\Models\Invoice; | ||||
| use App\Models\Project; | ||||
| use Illuminate\Http\Request; | ||||
| use App\Factory\QuoteFactory; | ||||
| use App\Filters\QuoteFilters; | ||||
| @ -33,6 +34,7 @@ use App\Transformers\QuoteTransformer; | ||||
| use App\Utils\Traits\GeneratesCounter; | ||||
| use Illuminate\Support\Facades\Storage; | ||||
| use App\Transformers\InvoiceTransformer; | ||||
| use App\Transformers\ProjectTransformer; | ||||
| use App\Factory\CloneQuoteToInvoiceFactory; | ||||
| use App\Factory\CloneQuoteToProjectFactory; | ||||
| use App\Http\Requests\Quote\EditQuoteRequest; | ||||
| @ -395,8 +397,8 @@ class QuoteController extends BaseController | ||||
|         $quote = $this->quote_repo->save($request->all(), $quote); | ||||
| 
 | ||||
|         $quote->service() | ||||
|               ->triggeredActions($request) | ||||
|               ->deletePdf(); | ||||
|               ->triggeredActions($request); | ||||
|             //   ->deletePdf();
 | ||||
| 
 | ||||
|         event(new QuoteWasUpdated($quote, $quote->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); | ||||
| 
 | ||||
| @ -524,16 +526,15 @@ class QuoteController extends BaseController | ||||
|             return response(['message' => 'Please verify your account to send emails.'], 400); | ||||
|         } | ||||
| 
 | ||||
|         $quotes = Quote::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get(); | ||||
|         $quotes = Quote::query()->with('invitations')->withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get(); | ||||
| 
 | ||||
|         if (! $quotes) { | ||||
|             return response()->json(['message' => ctrans('texts.quote_not_found')]); | ||||
|         } | ||||
| 
 | ||||
|         /* | ||||
|          * Download Invoice/s | ||||
|          * Download Quote/s | ||||
|          */ | ||||
| 
 | ||||
|         if ($action == 'bulk_download' && $quotes->count() >= 1) { | ||||
|             $quotes->each(function ($quote) use($user){ | ||||
|                 if ($user->cannot('view', $quote)) { | ||||
| @ -541,7 +542,7 @@ class QuoteController extends BaseController | ||||
|                 } | ||||
|             }); | ||||
| 
 | ||||
|             ZipQuotes::dispatch($quotes, $quotes->first()->company, auth()->user()); | ||||
|             ZipQuotes::dispatch($quotes->pluck('id')->toArray(), $quotes->first()->company, auth()->user()); | ||||
| 
 | ||||
|             return response()->json(['message' => ctrans('texts.sent_message')], 200); | ||||
|         } | ||||
| @ -556,12 +557,12 @@ class QuoteController extends BaseController | ||||
|                 } | ||||
|             }); | ||||
| 
 | ||||
|             return $this->listResponse(Quote::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()); | ||||
|             return $this->listResponse(Quote::query()->withTrashed()->whereIn('id', $this->transformKeys($ids))->company()); | ||||
|         } | ||||
| 
 | ||||
|         if ($action == 'bulk_print' && $user->can('view', $quotes->first())) { | ||||
|             $paths = $quotes->map(function ($quote) { | ||||
|                 return $quote->service()->getQuotePdf(); | ||||
|                 return (new \App\Jobs\Entity\CreateRawPdf($quote->invitations->first(), $quote->company->db))->handle(); | ||||
|             }); | ||||
| 
 | ||||
|             $merge = (new PdfMerge($paths->toArray()))->run(); | ||||
| @ -575,18 +576,13 @@ class QuoteController extends BaseController | ||||
|         if ($action == 'convert_to_project') { | ||||
|             $quotes->each(function ($quote, $key) use ($user) { | ||||
|                 if ($user->can('edit', $quote)) { | ||||
|                     $project = CloneQuoteToProjectFactory::create($quote, $user->id); | ||||
|                      | ||||
|                     if (empty($project->number)) { | ||||
|                         $project->number = $this->getNextProjectNumber($project); | ||||
|                     } | ||||
|                     $project->save(); | ||||
|                     $quote->project_id = $project->id; | ||||
|                     $quote->save(); | ||||
| 
 | ||||
|                     $quote->service()->convertToProject(); | ||||
| 
 | ||||
|                 } | ||||
|             }); | ||||
| 
 | ||||
|             return $this->listResponse(Quote::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()); | ||||
|             return $this->listResponse(Quote::query()->withTrashed()->whereIn('id', $this->transformKeys($ids))->company()); | ||||
|         } | ||||
| 
 | ||||
|         /* | ||||
| @ -684,7 +680,14 @@ class QuoteController extends BaseController | ||||
|     private function performAction(Quote $quote, $action, $bulk = false) | ||||
|     { | ||||
|         switch ($action) { | ||||
|             case 'convert': | ||||
|             case 'convert_to_project': | ||||
| 
 | ||||
|                 $this->entity_type = Project::class; | ||||
|                 $this->entity_transformer = ProjectTransformer::class; | ||||
| 
 | ||||
|                 return $this->itemResponse($quote->service()->convertToProject()); | ||||
| 
 | ||||
|                 case 'convert': | ||||
|             case 'convert_to_invoice': | ||||
| 
 | ||||
|                 $this->entity_type = Invoice::class; | ||||
| @ -692,8 +695,6 @@ class QuoteController extends BaseController | ||||
| 
 | ||||
|                 return $this->itemResponse($quote->service()->convertToInvoice()); | ||||
| 
 | ||||
|                 break; | ||||
| 
 | ||||
|             case 'clone_to_invoice': | ||||
| 
 | ||||
|                 $this->entity_type = Invoice::class; | ||||
| @ -702,41 +703,38 @@ class QuoteController extends BaseController | ||||
|                 $invoice = CloneQuoteToInvoiceFactory::create($quote, auth()->user()->id); | ||||
| 
 | ||||
|                 return $this->itemResponse($invoice); | ||||
|                 break; | ||||
| 
 | ||||
|             case 'clone_to_quote': | ||||
|                 $quote = CloneQuoteFactory::create($quote, auth()->user()->id); | ||||
| 
 | ||||
|                 return $this->itemResponse($quote); | ||||
|                 break; | ||||
| 
 | ||||
|             case 'approve': | ||||
|                 if (! in_array($quote->status_id, [Quote::STATUS_SENT, Quote::STATUS_DRAFT])) { | ||||
|                     return response()->json(['message' => ctrans('texts.quote_unapprovable')], 400); | ||||
|                 } | ||||
| 
 | ||||
|                 return $this->itemResponse($quote->service()->approveWithNoCoversion()->save()); | ||||
|                 break; | ||||
| 
 | ||||
|             case 'history': | ||||
|                 // code...
 | ||||
|                 break; | ||||
|             case 'download': | ||||
| 
 | ||||
|                 //$file = $quote->pdf_file_path();
 | ||||
|                 $file = $quote->service()->getQuotePdf(); | ||||
| 
 | ||||
|                 return response()->streamDownload(function () use ($file) { | ||||
|                     echo Storage::get($file); | ||||
|                 }, basename($file), ['Content-Type' => 'application/pdf']); | ||||
| 
 | ||||
| 
 | ||||
|                 break; | ||||
|             case 'restore': | ||||
|                 $this->quote_repo->restore($quote); | ||||
| 
 | ||||
|                 if (! $bulk) { | ||||
|                     return $this->itemResponse($quote); | ||||
|                 } | ||||
| 
 | ||||
|                 break; | ||||
| 
 | ||||
|             case 'archive': | ||||
|                 $this->quote_repo->archive($quote); | ||||
| 
 | ||||
| @ -754,16 +752,11 @@ class QuoteController extends BaseController | ||||
| 
 | ||||
|                 break; | ||||
|             case 'email': | ||||
|                 $quote->service()->sendEmail(); | ||||
| 
 | ||||
|                 return response()->json(['message'=> ctrans('texts.sent_message')], 200); | ||||
|                 break; | ||||
| 
 | ||||
|             case 'send_email': | ||||
| 
 | ||||
|                 $quote->service()->sendEmail(); | ||||
| 
 | ||||
|                 return response()->json(['message'=> ctrans('texts.sent_message')], 200); | ||||
|                 break; | ||||
| 
 | ||||
|             case 'mark_sent': | ||||
|                 $quote->service()->markSent()->save(); | ||||
|  | ||||
| @ -153,7 +153,10 @@ class RecurringInvoiceController extends BaseController | ||||
|      */ | ||||
|     public function create(CreateRecurringInvoiceRequest $request) | ||||
|     { | ||||
|         $recurring_invoice = RecurringInvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id); | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         $recurring_invoice = RecurringInvoiceFactory::create($user->company()->id, $user->id); | ||||
| 
 | ||||
|         return $this->itemResponse($recurring_invoice); | ||||
|     } | ||||
| @ -199,7 +202,10 @@ class RecurringInvoiceController extends BaseController | ||||
|      */ | ||||
|     public function store(StoreRecurringInvoiceRequest $request) | ||||
|     { | ||||
|         $recurring_invoice = $this->recurring_invoice_repo->save($request->all(), RecurringInvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id)); | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         $recurring_invoice = $this->recurring_invoice_repo->save($request->all(), RecurringInvoiceFactory::create($user->company()->id, $user->id)); | ||||
| 
 | ||||
|         $recurring_invoice->service() | ||||
|                           ->triggeredActions($request) | ||||
| @ -380,7 +386,7 @@ class RecurringInvoiceController extends BaseController | ||||
| 
 | ||||
|         $recurring_invoice->service() | ||||
|                           ->triggeredActions($request) | ||||
|                           ->deletePdf() | ||||
|                         //   ->deletePdf()
 | ||||
|                           ->save(); | ||||
| 
 | ||||
|         event(new RecurringInvoiceWasUpdated($recurring_invoice, $recurring_invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); | ||||
| @ -405,18 +411,21 @@ class RecurringInvoiceController extends BaseController | ||||
|      */ | ||||
|     public function bulk(BulkRecurringInvoiceRequest $request) | ||||
|     { | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         $percentage_increase = request()->has('percentage_increase') ? request()->input('percentage_increase') : 0; | ||||
| 
 | ||||
|         if (in_array($request->action, ['increase_prices', 'update_prices'])) { | ||||
|             UpdateRecurring::dispatch($request->ids, auth()->user()->company(), auth()->user(), $request->action, $percentage_increase); | ||||
|             UpdateRecurring::dispatch($request->ids, $user->company(), $user, $request->action, $percentage_increase); | ||||
| 
 | ||||
|             return response()->json(['message' => 'Update in progress.'], 200); | ||||
|         } | ||||
| 
 | ||||
|         $recurring_invoices = RecurringInvoice::withTrashed()->find($request->ids); | ||||
| 
 | ||||
|         $recurring_invoices->each(function ($recurring_invoice, $key) use ($request) { | ||||
|             if (auth()->user()->can('edit', $recurring_invoice)) { | ||||
|         $recurring_invoices->each(function ($recurring_invoice, $key) use ($request, $user) { | ||||
|             if ($user->can('edit', $recurring_invoice)) { | ||||
|                 $this->performAction($recurring_invoice, $request->action, true); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
| @ -11,12 +11,13 @@ | ||||
| 
 | ||||
| namespace App\Http\Controllers\Reports; | ||||
| 
 | ||||
| use Illuminate\Http\Response; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use App\Jobs\Report\SendToAdmin; | ||||
| use App\Export\CSV\DocumentExport; | ||||
| use App\Jobs\Report\PreviewReport; | ||||
| use App\Http\Controllers\BaseController; | ||||
| use App\Http\Requests\Report\GenericReportRequest; | ||||
| use App\Jobs\Report\SendToAdmin; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use Illuminate\Http\Response; | ||||
| 
 | ||||
| class DocumentReportController extends BaseController | ||||
| { | ||||
| @ -62,14 +63,27 @@ class DocumentReportController extends BaseController | ||||
|      */ | ||||
|     public function __invoke(GenericReportRequest $request) | ||||
|     { | ||||
| 
 | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         if ($request->has('send_email') && $request->get('send_email')) { | ||||
|             SendToAdmin::dispatch(auth()->user()->company(), $request->all(), DocumentExport::class, $this->filename); | ||||
|             SendToAdmin::dispatch($user->company(), $request->all(), DocumentExport::class, $this->filename); | ||||
| 
 | ||||
|             return response()->json(['message' => 'working...'], 200); | ||||
|         } | ||||
|         // expect a list of visible fields, or use the default
 | ||||
| 
 | ||||
|         $export = new DocumentExport(auth()->user()->company(), $request->all()); | ||||
|         if($request->has('output') && $request->input('output') == 'json') { | ||||
| 
 | ||||
|             $hash = \Illuminate\Support\Str::uuid(); | ||||
| 
 | ||||
|             PreviewReport::dispatch($user->company(), $request->all(), DocumentExport::class, $hash); | ||||
| 
 | ||||
|             return response()->json(['message' => $hash], 200); | ||||
|         } | ||||
| 
 | ||||
|         $export = new DocumentExport($user->company(), $request->all()); | ||||
| 
 | ||||
|         $csv = $export->run(); | ||||
| 
 | ||||
|  | ||||
| @ -80,7 +80,6 @@ class InvoiceItemReportController extends BaseController | ||||
| 
 | ||||
|             return response()->json(['message' => $hash], 200); | ||||
|         } | ||||
| 
 | ||||
|          | ||||
|         $export = new InvoiceItemExport($user->company(), $request->all()); | ||||
| 
 | ||||
|  | ||||
| @ -11,12 +11,13 @@ | ||||
| 
 | ||||
| namespace App\Http\Controllers\Reports; | ||||
| 
 | ||||
| use Illuminate\Http\Response; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use App\Jobs\Report\SendToAdmin; | ||||
| use App\Jobs\Report\PreviewReport; | ||||
| use App\Export\CSV\QuoteItemExport; | ||||
| use App\Http\Controllers\BaseController; | ||||
| use App\Http\Requests\Report\GenericReportRequest; | ||||
| use App\Jobs\Report\SendToAdmin; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use Illuminate\Http\Response; | ||||
| 
 | ||||
| class QuoteItemReportController extends BaseController | ||||
| { | ||||
| @ -62,14 +63,26 @@ class QuoteItemReportController extends BaseController | ||||
|      */ | ||||
|     public function __invoke(GenericReportRequest $request) | ||||
|     { | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         if ($request->has('send_email') && $request->get('send_email')) { | ||||
|             SendToAdmin::dispatch(auth()->user()->company(), $request->all(), QuoteItemExport::class, $this->filename); | ||||
|             SendToAdmin::dispatch($user->company(), $request->all(), QuoteItemExport::class, $this->filename); | ||||
| 
 | ||||
|             return response()->json(['message' => 'working...'], 200); | ||||
|         } | ||||
|         // expect a list of visible fields, or use the default
 | ||||
| 
 | ||||
|         $export = new QuoteItemExport(auth()->user()->company(), $request->all()); | ||||
|         if($request->has('output') && $request->input('output') == 'json') { | ||||
| 
 | ||||
|             $hash = \Illuminate\Support\Str::uuid(); | ||||
| 
 | ||||
|             PreviewReport::dispatch($user->company(), $request->all(), QuoteItemExport::class, $hash); | ||||
| 
 | ||||
|             return response()->json(['message' => $hash], 200); | ||||
|         } | ||||
| 
 | ||||
|         $export = new QuoteItemExport($user->company(), $request->all()); | ||||
| 
 | ||||
|         $csv = $export->run(); | ||||
| 
 | ||||
|  | ||||
| @ -11,12 +11,13 @@ | ||||
| 
 | ||||
| namespace App\Http\Controllers\Reports; | ||||
| 
 | ||||
| use App\Export\CSV\RecurringInvoiceExport; | ||||
| use App\Http\Controllers\BaseController; | ||||
| use App\Http\Requests\Report\GenericReportRequest; | ||||
| use App\Jobs\Report\SendToAdmin; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use Illuminate\Http\Response; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use App\Jobs\Report\SendToAdmin; | ||||
| use App\Jobs\Report\PreviewReport; | ||||
| use App\Http\Controllers\BaseController; | ||||
| use App\Export\CSV\RecurringInvoiceExport; | ||||
| use App\Http\Requests\Report\GenericReportRequest; | ||||
| 
 | ||||
| class RecurringInvoiceReportController extends BaseController | ||||
| { | ||||
| @ -31,14 +32,26 @@ class RecurringInvoiceReportController extends BaseController | ||||
| 
 | ||||
|     public function __invoke(GenericReportRequest $request) | ||||
|     { | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         if ($request->has('send_email') && $request->get('send_email')) { | ||||
|             SendToAdmin::dispatch(auth()->user()->company(), $request->all(), RecurringInvoiceExport::class, $this->filename); | ||||
|             SendToAdmin::dispatch($user->company(), $request->all(), RecurringInvoiceExport::class, $this->filename); | ||||
| 
 | ||||
|             return response()->json(['message' => 'working...'], 200); | ||||
|         } | ||||
|         // expect a list of visible fields, or use the default
 | ||||
| 
 | ||||
|         $export = new RecurringInvoiceExport(auth()->user()->company(), $request->all()); | ||||
|         if($request->has('output') && $request->input('output') == 'json') { | ||||
| 
 | ||||
|             $hash = \Illuminate\Support\Str::uuid(); | ||||
| 
 | ||||
|             PreviewReport::dispatch($user->company(), $request->all(), RecurringInvoiceExport::class, $hash); | ||||
| 
 | ||||
|             return response()->json(['message' => $hash], 200); | ||||
|         } | ||||
| 
 | ||||
|         $export = new RecurringInvoiceExport($user->company(), $request->all()); | ||||
| 
 | ||||
|         $csv = $export->run(); | ||||
| 
 | ||||
|  | ||||
| @ -11,12 +11,13 @@ | ||||
| 
 | ||||
| namespace App\Http\Controllers\Reports; | ||||
| 
 | ||||
| use Illuminate\Http\Response; | ||||
| use App\Export\CSV\TaskExport; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use App\Jobs\Report\SendToAdmin; | ||||
| use App\Jobs\Report\PreviewReport; | ||||
| use App\Http\Controllers\BaseController; | ||||
| use App\Http\Requests\Report\GenericReportRequest; | ||||
| use App\Jobs\Report\SendToAdmin; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use Illuminate\Http\Response; | ||||
| 
 | ||||
| class TaskReportController extends BaseController | ||||
| { | ||||
| @ -62,14 +63,26 @@ class TaskReportController extends BaseController | ||||
|      */ | ||||
|     public function __invoke(GenericReportRequest $request) | ||||
|     { | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         if ($request->has('send_email') && $request->get('send_email')) { | ||||
|             SendToAdmin::dispatch(auth()->user()->company(), $request->all(), TaskExport::class, $this->filename); | ||||
|             SendToAdmin::dispatch($user->company(), $request->all(), TaskExport::class, $this->filename); | ||||
| 
 | ||||
|             return response()->json(['message' => 'working...'], 200); | ||||
|         } | ||||
|         // expect a list of visible fields, or use the default
 | ||||
| 
 | ||||
|         $export = new TaskExport(auth()->user()->company(), $request->all()); | ||||
|         if($request->has('output') && $request->input('output') == 'json') { | ||||
| 
 | ||||
|             $hash = \Illuminate\Support\Str::uuid(); | ||||
| 
 | ||||
|             PreviewReport::dispatch($user->company(), $request->all(), TaskExport::class, $hash); | ||||
| 
 | ||||
|             return response()->json(['message' => $hash], 200); | ||||
|         } | ||||
| 
 | ||||
|         $export = new TaskExport($user->company(), $request->all()); | ||||
| 
 | ||||
|         $csv = $export->run(); | ||||
| 
 | ||||
|  | ||||
| @ -11,11 +11,12 @@ | ||||
| 
 | ||||
| namespace App\Http\Controllers\Reports; | ||||
| 
 | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use App\Export\CSV\VendorExport; | ||||
| use App\Jobs\Report\SendToAdmin; | ||||
| use App\Jobs\Report\PreviewReport; | ||||
| use App\Http\Controllers\BaseController; | ||||
| use App\Http\Requests\Report\GenericReportRequest; | ||||
| use App\Jobs\Report\SendToAdmin; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| 
 | ||||
| class VendorReportController extends BaseController | ||||
| { | ||||
| @ -30,14 +31,26 @@ class VendorReportController extends BaseController | ||||
| 
 | ||||
|     public function __invoke(GenericReportRequest $request) | ||||
|     { | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         if ($request->has('send_email') && $request->get('send_email')) { | ||||
|             SendToAdmin::dispatch(auth()->user()->company(), $request->all(), VendorExport::class, $this->filename); | ||||
|             SendToAdmin::dispatch($user->company(), $request->all(), VendorExport::class, $this->filename); | ||||
| 
 | ||||
|             return response()->json(['message' => 'working...'], 200); | ||||
|         } | ||||
|         // expect a list of visible fields, or use the default
 | ||||
| 
 | ||||
|         $export = new VendorExport(auth()->user()->company(), $request->all()); | ||||
|         if($request->has('output') && $request->input('output') == 'json') { | ||||
| 
 | ||||
|             $hash = \Illuminate\Support\Str::uuid(); | ||||
| 
 | ||||
|             PreviewReport::dispatch($user->company(), $request->all(), VendorExport::class, $hash); | ||||
| 
 | ||||
|             return response()->json(['message' => $hash], 200); | ||||
|         } | ||||
| 
 | ||||
|         $export = new VendorExport($user->company(), $request->all()); | ||||
| 
 | ||||
|         $csv = $export->run(); | ||||
| 
 | ||||
|  | ||||
| @ -11,85 +11,102 @@ | ||||
| 
 | ||||
| namespace App\Http\Controllers; | ||||
| 
 | ||||
| use App\Models\User; | ||||
| use App\Models\Client; | ||||
| use App\Models\ClientContact; | ||||
| use App\Http\Requests\Search\GenericSearchRequest; | ||||
| use App\Models\Client; | ||||
| use App\Models\Invoice; | ||||
| use App\Models\User; | ||||
| 
 | ||||
| class SearchController extends Controller | ||||
| { | ||||
|     private array $clients = []; | ||||
| 
 | ||||
|     private array $client_contacts = []; | ||||
| 
 | ||||
|     private array $invoices = []; | ||||
| 
 | ||||
|     public function __invoke(GenericSearchRequest $request) | ||||
|     { | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         $this->clientMap($user); | ||||
|         $this->invoiceMap($user); | ||||
|          | ||||
|         return response()->json([ | ||||
|             'clients' => $this->clientMap($user), | ||||
|             'client_contacts' => $this->clientContactMap($user), | ||||
|             'invoices' => $this->invoiceMap($user), | ||||
|             'clients' => $this->clients, | ||||
|             'client_contacts' => $this->client_contacts, | ||||
|             'invoices' => $this->invoices, | ||||
|             'settings' => $this->settingsMap(), | ||||
|         ], 200); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private function clientMap(User $user) { | ||||
|     private function clientMap(User $user) | ||||
|     { | ||||
| 
 | ||||
|         return Client::query() | ||||
|         $clients =  Client::query() | ||||
|                      ->company() | ||||
|                      ->when($user->cannot('view_all') || $user->cannot('view_client'), function ($query) use($user) { | ||||
|                      ->where('is_deleted', 0) | ||||
|                      ->when(!$user->hasPermission('view_all') || !$user->hasPermission('view_client'), function ($query) use ($user) { | ||||
|                          $query->where('user_id', $user->id); | ||||
|                      }) | ||||
|                      ->cursor() | ||||
|                      ->map(function ($client){ | ||||
|                         return [ | ||||
|                             'name' => $client->present()->name(),  | ||||
|                             'type' => '/client',  | ||||
|                             'id' => $client->hashed_id, | ||||
|                             'path' => "/clients/{$client->hashed_id}/edit" | ||||
|                         ]; | ||||
|                      }); | ||||
|                      ->orderBy('id', 'desc') | ||||
|                      ->take(1000) | ||||
|                      ->get(); | ||||
| 
 | ||||
|                         foreach($clients as $client) { | ||||
|                             $this->clients[] = [ | ||||
|                                 'name' => $client->present()->name(), | ||||
|                                 'type' => '/client', | ||||
|                                 'id' => $client->hashed_id, | ||||
|                                 'path' => "/clients/{$client->hashed_id}/edit" | ||||
|                             ]; | ||||
| 
 | ||||
|                             $client->contacts->each(function ($contact) { | ||||
|                                 $this->client_contacts[] = [ | ||||
|                                     'name' => $contact->present()->search_display(), | ||||
|                                     'type' => '/client_contact', | ||||
|                                     'id' => $contact->hashed_id, | ||||
|                                     'path' => "/clients/{$contact->hashed_id}" | ||||
|                                 ]; | ||||
| 
 | ||||
|                                                  | ||||
|                             }); | ||||
|                         } | ||||
|                           | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private function clientContactMap(User $user) { | ||||
|     private function invoiceMap(User $user) | ||||
|     { | ||||
| 
 | ||||
|         return ClientContact::query() | ||||
|         $invoices = Invoice::query() | ||||
|                      ->company() | ||||
|                      ->with('client') | ||||
|                      ->when($user->cannot('view_all') || $user->cannot('view_client'), function ($query) use($user) { | ||||
|                      ->where('is_deleted', 0) | ||||
|                      ->whereHas('client', function ($q) { | ||||
|                          $q->where('is_deleted', 0); | ||||
|                      }) | ||||
|                      ->when(!$user->hasPermission('view_all') || !$user->hasPermission('view_invoice'), function ($query) use ($user) { | ||||
|                          $query->where('user_id', $user->id); | ||||
|                      }) | ||||
|                      ->cursor() | ||||
|                      ->map(function ($contact){ | ||||
|                         return [ | ||||
|                             'name' => $contact->present()->search_display(),  | ||||
|                             'type' => '/client_contact',  | ||||
|                             'id' => $contact->client->hashed_id, | ||||
|                             'path' => "/clients/{$contact->client->hashed_id}" | ||||
|                         ]; | ||||
|                      }); | ||||
|                      ->orderBy('id', 'desc') | ||||
|                     ->take(3000) | ||||
|                     ->get();  | ||||
|                      | ||||
|                     foreach($invoices as $invoice) { | ||||
|                             $this->invoices[] = [ | ||||
|                                 'name' => $invoice->client->present()->name() . ' - ' . $invoice->number, | ||||
|                                 'type' => '/invoice', | ||||
|                                 'id' => $invoice->hashed_id, | ||||
|                                 'path' => "/clients/{$invoice->hashed_id}/edit" | ||||
|                             ]; | ||||
|                     } | ||||
|                      | ||||
|     } | ||||
| 
 | ||||
|     private function invoiceMap(User $user) { | ||||
| 
 | ||||
|         return Invoice::query() | ||||
|                      ->company() | ||||
|                      ->with('client') | ||||
|                      ->when($user->cannot('view_all') || $user->cannot('view_invoice'), function ($query) use($user) { | ||||
|                          $query->where('user_id', $user->id); | ||||
|                      }) | ||||
|                      ->cursor() | ||||
|                      ->map(function ($invoice){ | ||||
|                         return [ | ||||
|                             'name' => $invoice->client->present()->name() . ' - ' . $invoice->number,  | ||||
|                             'type' => '/invoice',  | ||||
|                             'id' => $invoice->hashed_id, | ||||
|                             'path' => "/clients/{$invoice->hashed_id}/edit" | ||||
|                         ]; | ||||
|                      }); | ||||
|     } | ||||
| 
 | ||||
|     private function settingsMap() { | ||||
|     private function settingsMap() | ||||
|     { | ||||
| 
 | ||||
|         $paths = [ | ||||
|             'user_details' => '/settings/user_details', | ||||
|  | ||||
| @ -11,13 +11,14 @@ | ||||
| 
 | ||||
| namespace App\Http\Controllers; | ||||
| 
 | ||||
| use App\Exceptions\FilePermissionsFailure; | ||||
| use App\Utils\Ninja; | ||||
| use App\Models\Company; | ||||
| use App\Utils\Traits\AppSetup; | ||||
| use App\Utils\Traits\ClientGroupSettingsSaver; | ||||
| use Illuminate\Foundation\Bus\DispatchesJobs; | ||||
| use Illuminate\Support\Facades\Artisan; | ||||
| use Illuminate\Support\Facades\Storage; | ||||
| use App\Exceptions\FilePermissionsFailure; | ||||
| use Illuminate\Foundation\Bus\DispatchesJobs; | ||||
| use App\Utils\Traits\ClientGroupSettingsSaver; | ||||
| 
 | ||||
| class SelfUpdateController extends BaseController | ||||
| { | ||||
| @ -109,11 +110,33 @@ class SelfUpdateController extends BaseController | ||||
| 
 | ||||
|         $this->buildCache(true); | ||||
| 
 | ||||
|         $this->runModelChecks(); | ||||
| 
 | ||||
|         nlog('Called Artisan commands'); | ||||
| 
 | ||||
|         return response()->json(['message' => 'Update completed'], 200); | ||||
|     } | ||||
| 
 | ||||
|     private function runModelChecks() | ||||
|     { | ||||
|         Company::query() | ||||
|                ->cursor() | ||||
|                ->each(function ($company){ | ||||
| 
 | ||||
|                 $settings = $company->settings; | ||||
| 
 | ||||
|                 if(property_exists($settings->pdf_variables, 'purchase_order_details')) | ||||
|                     return; | ||||
| 
 | ||||
|                     $pdf_variables = $settings->pdf_variables; | ||||
|                     $pdf_variables->purchase_order_details = []; | ||||
|                     $settings->pdf_variables = $pdf_variables; | ||||
|                     $company->settings = $settings; | ||||
|                     $company->save(); | ||||
| 
 | ||||
|                }); | ||||
|     } | ||||
| 
 | ||||
|     private function clearCacheDir() | ||||
|     { | ||||
|         $directoryIterator = new \RecursiveDirectoryIterator(base_path('bootstrap/cache'), \RecursiveDirectoryIterator::SKIP_DOTS); | ||||
|  | ||||
| @ -15,11 +15,9 @@ use App\DataMapper\FeesAndLimits; | ||||
| use App\Factory\CompanyGatewayFactory; | ||||
| use App\Http\Requests\StripeConnect\InitializeStripeConnectRequest; | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Models\Client; | ||||
| use App\Models\Company; | ||||
| use App\Models\CompanyGateway; | ||||
| use App\Models\GatewayType; | ||||
| use App\PaymentDrivers\Stripe\Jobs\StripeWebhook; | ||||
| use Stripe\Exception\ApiErrorException; | ||||
| 
 | ||||
| class StripeConnectController extends BaseController | ||||
| @ -124,12 +122,27 @@ class StripeConnectController extends BaseController | ||||
|         $company_gateway->setConfig($payload); | ||||
|         $company_gateway->save(); | ||||
| 
 | ||||
|         try{ | ||||
|             $stripe = $company_gateway->driver()->init(); | ||||
|             $a = \Stripe\Account::retrieve($response->stripe_user_id, $stripe->stripe_connect_auth); | ||||
|              | ||||
|             if($a->business_name ?? false) { | ||||
|                 $company_gateway->label = substr("Stripe - {$a->business_name}", 0, 250); | ||||
|                 $company_gateway->save(); | ||||
|             } | ||||
|         } | ||||
|         catch(\Exception $e){ | ||||
|             nlog("could not harvest stripe company name"); | ||||
|         } | ||||
| 
 | ||||
|         // nlog("Stripe Connect Redirect URI = {$redirect_uri}");
 | ||||
| 
 | ||||
|         // StripeWebhook::dispatch($company->company_key, $company_gateway->id);
 | ||||
|         // if(isset($request->getTokenContent()['is_react']) && $request->getTokenContent()['is_react']) {
 | ||||
|         if(isset($request->getTokenContent()['is_react']) && $request->getTokenContent()['is_react']) { | ||||
|             $redirect_uri = 'https://app.invoicing.co/#/settings/online_payments'; | ||||
|         // } else {
 | ||||
|         //     $redirect_uri = 'https://invoicing.co/stripe/completed';
 | ||||
|         // }
 | ||||
|         } else { | ||||
|             $redirect_uri = 'https://invoicing.co/stripe/completed'; | ||||
|         } | ||||
| 
 | ||||
|         //response here
 | ||||
|         return view('auth.connect.completed', ['url' => $redirect_uri]); | ||||
|  | ||||
| @ -21,7 +21,6 @@ use App\Jobs\Invoice\InjectSignature; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
| use Illuminate\Contracts\View\Factory; | ||||
| use App\Models\PurchaseOrderInvitation; | ||||
| use Illuminate\Support\Facades\Storage; | ||||
| use App\Events\Misc\InvitationWasViewed; | ||||
| use App\Jobs\Vendor\CreatePurchaseOrderPdf; | ||||
| use App\Events\PurchaseOrder\PurchaseOrderWasViewed; | ||||
| @ -119,14 +118,8 @@ class PurchaseOrderController extends Controller | ||||
| 
 | ||||
|         $file = (new CreatePurchaseOrderPdf($invitation, $invitation->company->db))->rawPdf(); | ||||
| 
 | ||||
|         // $headers = ['Content-Type' => 'application/pdf'];
 | ||||
|         // $entity_string = $data['entity_type'];
 | ||||
|         // $file_name = $invitation->{$entity_string}->numberFormatter().'.pdf';
 | ||||
|         // return response()->streamDownload(function () use ($file) {
 | ||||
|         //     echo $file;
 | ||||
|         // }, $file_name, $headers);
 | ||||
| 
 | ||||
|         $headers = ['Content-Type' => 'application/pdf']; | ||||
| 
 | ||||
|         return response()->make($file, 200, $headers); | ||||
| 
 | ||||
|     } | ||||
| @ -156,7 +149,7 @@ class PurchaseOrderController extends Controller | ||||
|         $transformed_ids = $this->transformKeys($request->purchase_orders); | ||||
| 
 | ||||
|         if ($request->input('action') == 'download') { | ||||
|             return $this->downloadInvoices((array) $transformed_ids); | ||||
|             return $this->downloadPurchaseOrders((array) $transformed_ids); | ||||
|         } elseif ($request->input('action') == 'accept') { | ||||
|             return $this->acceptPurchaseOrder($request->all()); | ||||
|         } | ||||
| @ -177,7 +170,8 @@ class PurchaseOrderController extends Controller | ||||
|         $purchase_count_query = clone $purchase_orders; | ||||
| 
 | ||||
|         $purchase_orders->whereIn('status_id', [PurchaseOrder::STATUS_DRAFT, PurchaseOrder::STATUS_SENT]) | ||||
|                         ->cursor()->each(function ($purchase_order) { | ||||
|                         ->cursor() | ||||
|                         ->each(function ($purchase_order) { | ||||
|              | ||||
|                             $purchase_order->service() | ||||
|                                         ->markSent() | ||||
| @ -201,39 +195,39 @@ class PurchaseOrderController extends Controller | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function downloadInvoices($ids) | ||||
|     public function downloadPurchaseOrders($ids) | ||||
|     { | ||||
|         $purchase_orders = PurchaseOrder::query() | ||||
|                             ->whereIn('id', $ids) | ||||
|                             ->where('vendor_id', auth()->guard('vendor')->user()->vendor_id) | ||||
|         $purchase_order_invitations = PurchaseOrderInvitation::query() | ||||
|                             ->with('purchase_order', 'company') | ||||
|                             ->whereIn('purchase_order_id', $ids) | ||||
|                             ->where('vendor_contact_id', auth()->guard('vendor')->user()->id) | ||||
|                             ->withTrashed() | ||||
|                             ->get(); | ||||
| 
 | ||||
|         if (count($purchase_orders) == 0) { | ||||
|         if (count($purchase_order_invitations) == 0) { | ||||
|             return back()->with(['message' => ctrans('texts.no_items_selected')]); | ||||
|         } | ||||
| 
 | ||||
|         if (count($purchase_orders) == 1) { | ||||
|             $purchase_order = $purchase_orders->first(); | ||||
| 
 | ||||
|             $file = $purchase_order->service()->getPurchaseOrderPdf(auth()->guard('vendor')->user()); | ||||
|         if (count($purchase_order_invitations) == 1) { | ||||
| 
 | ||||
|             $invitation = $purchase_order_invitations->first(); | ||||
|             $file = (new CreatePurchaseOrderPdf($invitation, $invitation->company->db))->rawPdf(); | ||||
|             return response()->streamDownload(function () use ($file) { | ||||
|                 echo Storage::get($file); | ||||
|             }, basename($file), ['Content-Type' => 'application/pdf']); | ||||
|                 echo $file; | ||||
|             }, $invitation->purchase_order->numberFormatter().".pdf", ['Content-Type' => 'application/pdf']); | ||||
|         } | ||||
| 
 | ||||
|         return $this->buildZip($purchase_orders); | ||||
|         return $this->buildZip($purchase_order_invitations); | ||||
|     } | ||||
| 
 | ||||
|     private function buildZip($purchase_orders) | ||||
|     private function buildZip($invitations) | ||||
|     { | ||||
|         // create new archive
 | ||||
|         $zipFile = new \PhpZip\ZipFile(); | ||||
|         try { | ||||
|             foreach ($purchase_orders as $purchase_order) { | ||||
|                 //add it to the zip
 | ||||
|                 $zipFile->addFromString(basename($purchase_order->pdf_file_path()), file_get_contents($purchase_order->pdf_file_path(null, 'url', true))); | ||||
|             foreach ($invitations as $invitation) { | ||||
|                 $file = (new CreatePurchaseOrderPdf($invitation, $invitation->company->db))->rawPdf(); | ||||
|                 $zipFile->addFromString($invitation->purchase_order->numberFormatter().".pdf", $file); | ||||
|             } | ||||
| 
 | ||||
|             $filename = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.purchase_orders')).'.zip'; | ||||
|  | ||||
| @ -195,15 +195,15 @@ class PdfSlot extends Component | ||||
| 
 | ||||
|         } | ||||
|         elseif($this->entity_type == 'quote'){ | ||||
|             foreach($this->settings->pdf_variables->quote_details as $variable) | ||||
|             foreach($this->settings->pdf_variables->quote_details ?? [] as $variable) | ||||
|                 $entity_details .= "<div class='flex px-5 block'><p class= w-36 block'>{$variable}_label</p><p class='pl-5 w-36 block entity-field'>{$variable}</p></div>"; | ||||
|         } | ||||
|         elseif($this->entity_type == 'credit') { | ||||
|             foreach($this->settings->pdf_variables->credit_details as $variable) | ||||
|             foreach($this->settings->pdf_variables->credit_details ?? [] as $variable) | ||||
|                 $entity_details .= "<div class='flex px-5 block'><p class= w-36 block'>{$variable}_label</p><p class='pl-5 w-36 block entity-field'>{$variable}</p></div>"; | ||||
|         } | ||||
|         elseif($this->entity_type == 'purchase_order'){ | ||||
|             foreach($this->settings->pdf_variables->purchase_order_details as $variable) | ||||
|             foreach($this->settings->pdf_variables->purchase_order_details ?? [] as $variable) | ||||
|                 $entity_details .= "<div class='flex px-5 block'><p class= w-36 block'>{$variable}_label</p><p class='pl-5 w-36 block entity-field'>{$variable}</p></div>"; | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -22,7 +22,10 @@ class BulkBankTransactionRuleRequest extends Request | ||||
|      */ | ||||
|     public function authorize() : bool | ||||
|     { | ||||
|         return auth()->user()->isAdmin(); | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         return $user->isAdmin(); | ||||
|     } | ||||
| 
 | ||||
|     public function rules() | ||||
|  | ||||
| @ -93,7 +93,8 @@ class StoreClientRequest extends Request | ||||
| 
 | ||||
|         $rules['number'] = ['bail', 'nullable', Rule::unique('clients')->where('company_id', $user->company()->id)]; | ||||
|         $rules['id_number'] = ['bail', 'nullable', Rule::unique('clients')->where('company_id', $user->company()->id)]; | ||||
| 
 | ||||
|         $rules['classification'] = 'bail|sometimes|nullable|in:individual,company,partnership,trust,charity,government,other'; | ||||
|          | ||||
|         return $rules; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -60,6 +60,7 @@ class UpdateClientRequest extends Request | ||||
|         $rules['size_id'] = 'integer|nullable'; | ||||
|         $rules['country_id'] = 'integer|nullable'; | ||||
|         $rules['shipping_country_id'] = 'integer|nullable'; | ||||
|         $rules['classification'] = 'bail|sometimes|nullable|in:individual,company,partnership,trust,charity,government,other'; | ||||
| 
 | ||||
|         if ($this->id_number) { | ||||
|             $rules['id_number'] = Rule::unique('clients')->where('company_id', $user->company()->id)->ignore($this->client->id); | ||||
|  | ||||
| @ -25,7 +25,10 @@ class UpdateCompanyUserRequest extends Request | ||||
|      */ | ||||
|     public function authorize() : bool | ||||
|     { | ||||
|         return auth()->user()->isAdmin() || (auth()->user()->id == $this->user->id); | ||||
|         /** @var \App\Models\User $auth_user */ | ||||
|         $auth_user = auth()->user(); | ||||
| 
 | ||||
|         return $auth_user->isAdmin() || ($auth_user->id == $this->user->id); | ||||
|     } | ||||
| 
 | ||||
|     public function rules() | ||||
|  | ||||
| @ -24,16 +24,19 @@ class UpdateGroupSettingRequest extends Request | ||||
|      */ | ||||
|     public function authorize() : bool | ||||
|     { | ||||
|         return auth()->user()->can('edit', $this->group_setting); | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         return $user->can('edit', $this->group_setting); | ||||
|     } | ||||
| 
 | ||||
|     public function rules() | ||||
|     { | ||||
|         $rules['settings'] = new ValidClientGroupSettingsRule(); | ||||
|          | ||||
|         return [ | ||||
|             'settings' => [new ValidClientGroupSettingsRule()], | ||||
|         ]; | ||||
| 
 | ||||
| //        $rules['name'] = 'unique:group_settings,name,'.$this->id.',id,company_id,'.$this->group_setting->company_id;
 | ||||
| 
 | ||||
|         return $rules; | ||||
|     } | ||||
| 
 | ||||
|     public function prepareForValidation() | ||||
|  | ||||
| @ -30,18 +30,25 @@ class UpdatePaymentRequest extends Request | ||||
|      */ | ||||
|     public function authorize() : bool | ||||
|     { | ||||
|         return auth()->user()->can('edit', $this->payment); | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         return $user->can('edit', $this->payment); | ||||
|     } | ||||
| 
 | ||||
|     public function rules() | ||||
|     { | ||||
| 
 | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         $rules = [ | ||||
|             'invoices' => ['array', new PaymentAppliedValidAmount($this->all()), new ValidCreditsPresentRule($this->all())], | ||||
|             'invoices.*.invoice_id' => 'distinct', | ||||
|         ]; | ||||
| 
 | ||||
|         if ($this->number) { | ||||
|             $rules['number'] = Rule::unique('payments')->where('company_id', auth()->user()->company()->id)->ignore($this->payment->id); | ||||
|             $rules['number'] = Rule::unique('payments')->where('company_id', $user->company()->id)->ignore($this->payment->id); | ||||
|         } | ||||
| 
 | ||||
|         if ($this->file('documents') && is_array($this->file('documents'))) { | ||||
| @ -75,7 +82,8 @@ class UpdatePaymentRequest extends Request | ||||
| 
 | ||||
|         if (isset($input['invoices']) && is_array($input['invoices']) !== false) { | ||||
|             foreach ($input['invoices'] as $key => $value) { | ||||
|                 if (array_key_exists('invoice_id', $input['invoices'][$key])) { | ||||
|                 if(isset($input['invoices'][$key]['invoice_id'])){ | ||||
|                 // if (array_key_exists('invoice_id', $input['invoices'][$key])) {
 | ||||
|                     $input['invoices'][$key]['invoice_id'] = $this->decodePrimaryKey($value['invoice_id']); | ||||
|                 } | ||||
|             } | ||||
| @ -83,7 +91,8 @@ class UpdatePaymentRequest extends Request | ||||
| 
 | ||||
|         if (isset($input['credits']) && is_array($input['credits']) !== false) { | ||||
|             foreach ($input['credits'] as $key => $value) { | ||||
|                 if (array_key_exists('credits', $input['credits'][$key])) { | ||||
|                 // if (array_key_exists('credits', $input['credits'][$key])) {
 | ||||
|                 if (isset($input['credits'][$key]['credit_id'])) { | ||||
|                     $input['credits'][$key]['credit_id'] = $this->decodePrimaryKey($value['credit_id']); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @ -30,9 +30,11 @@ class BulkActionQuoteRequest extends Request | ||||
|     { | ||||
|         $input = $this->all(); | ||||
| 
 | ||||
|         $rules = []; | ||||
|         $rules = [ | ||||
|             'action' => 'sometimes|in:convert_to_invoice,convert_to_project,email,bulk_download,bulk_print,clone_to_invoice,approve,download,restore,archive,delete,send_email,mark_sent', | ||||
|         ]; | ||||
| 
 | ||||
|         if ($input['action'] == 'convert_to_invoice') { | ||||
|         if (in_array($input['action'], ['convert,convert_to_invoice']) ) { | ||||
|             $rules['action'] = [new ConvertableQuoteRule()]; | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -75,6 +75,9 @@ class UpdateRecurringExpenseRequest extends Request | ||||
| 
 | ||||
|     public function prepareForValidation() | ||||
|     { | ||||
|         /** @var \App\Models\User $user*/ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         $input = $this->all(); | ||||
| 
 | ||||
|         $input = $this->decodePrimaryKeys($input); | ||||
| @ -88,7 +91,7 @@ class UpdateRecurringExpenseRequest extends Request | ||||
|         } | ||||
| 
 | ||||
|         if (! array_key_exists('currency_id', $input) || strlen($input['currency_id']) == 0) { | ||||
|             $input['currency_id'] = (string) auth()->user()->company()->settings->currency_id; | ||||
|             $input['currency_id'] = (string) $user->company()->settings->currency_id; | ||||
|         } | ||||
| 
 | ||||
|         $this->replace($input); | ||||
|  | ||||
| @ -28,23 +28,30 @@ class StoreTaskRequest extends Request | ||||
|      */ | ||||
|     public function authorize() : bool | ||||
|     { | ||||
|         return auth()->user()->can('create', Task::class); | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         return $user->can('create', Task::class); | ||||
|     } | ||||
| 
 | ||||
|     public function rules() | ||||
|     { | ||||
| 
 | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         $rules = []; | ||||
| 
 | ||||
|         if (isset($this->number)) { | ||||
|             $rules['number'] = Rule::unique('tasks')->where('company_id', auth()->user()->company()->id); | ||||
|             $rules['number'] = Rule::unique('tasks')->where('company_id', $user->company()->id); | ||||
|         } | ||||
| 
 | ||||
|         if (isset($this->client_id)) { | ||||
|             $rules['client_id'] = 'bail|required|exists:clients,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; | ||||
|             $rules['client_id'] = 'bail|required|exists:clients,id,company_id,'.$user->company()->id.',is_deleted,0'; | ||||
|         } | ||||
| 
 | ||||
|         if (isset($this->project_id)) { | ||||
|             $rules['project_id'] = 'bail|required|exists:projects,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; | ||||
|             $rules['project_id'] = 'bail|required|exists:projects,id,company_id,'.$user->company()->id.',is_deleted,0'; | ||||
|         } | ||||
| 
 | ||||
|         $rules['timelog'] = ['bail','array',function ($attribute, $values, $fail) { | ||||
| @ -77,9 +84,9 @@ class StoreTaskRequest extends Request | ||||
| 
 | ||||
|     public function prepareForValidation() | ||||
|     { | ||||
|         $input = $this->all(); | ||||
|         $input = $this->decodePrimaryKeys($this->all()); | ||||
| 
 | ||||
|         $input = $this->decodePrimaryKeys($this->all()); | ||||
|          | ||||
|         if (array_key_exists('status_id', $input) && is_string($input['status_id'])) { | ||||
|             $input['status_id'] = $this->decodePrimaryKey($input['status_id']); | ||||
|         } | ||||
|  | ||||
| @ -22,14 +22,20 @@ class StoreTaxRateRequest extends Request | ||||
|      */ | ||||
|     public function authorize() : bool | ||||
|     { | ||||
|         return auth()->user()->isAdmin(); | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         return $user->isAdmin(); | ||||
|     } | ||||
| 
 | ||||
|     public function rules() | ||||
|     { | ||||
| 
 | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         return [ | ||||
|             //'name' => 'required',
 | ||||
|             'name' => 'required|unique:tax_rates,name,null,null,company_id,'.auth()->user()->companyId().',deleted_at,NULL', | ||||
|             'name' => 'required|unique:tax_rates,name,null,null,company_id,'.$user->companyId().',deleted_at,NULL', | ||||
|             'rate' => 'required|numeric', | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
| @ -23,17 +23,25 @@ class UpdateTaxRateRequest extends Request | ||||
|      */ | ||||
|     public function authorize() : bool | ||||
|     { | ||||
|         return auth()->user()->isAdmin(); | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         return $user->isAdmin(); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public function rules() | ||||
|     { | ||||
| 
 | ||||
|         /** @var \App\Models\User $user */ | ||||
|         $user = auth()->user(); | ||||
| 
 | ||||
|         $rules = []; | ||||
| 
 | ||||
|         $rules['rate'] = 'numeric'; | ||||
|         $rules['rate'] = 'sometimes|numeric'; | ||||
| 
 | ||||
|         if ($this->number) { | ||||
|             $rules['number'] = Rule::unique('tax_rates')->where('company_id', auth()->user()->company()->id)->ignore($this->tax_rate->id); | ||||
|         if ($this->name) { | ||||
|             $rules['name'] = Rule::unique('tax_rates')->where('company_id', $user->company()->id)->ignore($this->tax_rate->id); | ||||
|         } | ||||
| 
 | ||||
|         return $rules; | ||||
|  | ||||
| @ -60,6 +60,7 @@ class StoreVendorRequest extends Request | ||||
|         } | ||||
| 
 | ||||
|         $rules['language_id'] = 'bail|nullable|sometimes|exists:languages,id'; | ||||
|         $rules['classification'] = 'bail|sometimes|nullable|in:individual,company,partnership,trust,charity,government,other'; | ||||
| 
 | ||||
|         return $rules; | ||||
|     } | ||||
|  | ||||
| @ -61,6 +61,7 @@ class UpdateVendorRequest extends Request | ||||
|         } | ||||
| 
 | ||||
|         $rules['language_id'] = 'bail|nullable|sometimes|exists:languages,id'; | ||||
|         $rules['classification'] = 'bail|sometimes|nullable|in:individual,company,partnership,trust,charity,government,other'; | ||||
| 
 | ||||
|         return $rules; | ||||
|     } | ||||
|  | ||||
| @ -90,6 +90,12 @@ class PaymentAppliedValidAmount implements Rule | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if(count($this->input['invoices']) >=1 && $payment->status_id == Payment::STATUS_PENDING){ | ||||
|                 $this->message = 'Cannot apply a payment until the status is completed.'; | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         if (round($payment_amounts, 3) >= round($invoice_amounts, 3)) { | ||||
|  | ||||
| @ -18,6 +18,7 @@ use App\Jobs\Util\UnlinkFile; | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Mail\DownloadCredits; | ||||
| use App\Models\Company; | ||||
| use App\Models\CreditInvitation; | ||||
| use App\Models\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| @ -30,32 +31,12 @@ class ZipCredits implements ShouldQueue | ||||
| { | ||||
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||||
| 
 | ||||
|     public $credits; | ||||
| 
 | ||||
|     private $company; | ||||
| 
 | ||||
|     private $user; | ||||
| 
 | ||||
|     public $settings; | ||||
| 
 | ||||
|     public $tries = 1; | ||||
| 
 | ||||
|     /** | ||||
|      * @param $invoices | ||||
|      * @param Company $company | ||||
|      * @param $email | ||||
|      * @deprecated confirm to be deleted | ||||
|      * Create a new job instance. | ||||
|      */ | ||||
|     public function __construct($credits, Company $company, User $user) | ||||
|     public function __construct(protected array $credit_ids, protected Company $company, protected User $user) | ||||
|     { | ||||
|         $this->credits = $credits; | ||||
| 
 | ||||
|         $this->company = $company; | ||||
| 
 | ||||
|         $this->user = $user; | ||||
| 
 | ||||
|         $this->settings = $company->settings; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -67,21 +48,18 @@ class ZipCredits implements ShouldQueue | ||||
|     { | ||||
|         MultiDB::setDb($this->company->db); | ||||
| 
 | ||||
|         // create new zip object
 | ||||
|         $this->settings = $this->company->settings; | ||||
|         $zipFile = new \PhpZip\ZipFile(); | ||||
|         $file_name = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.credits')).'.zip'; | ||||
|         $invitation = $this->credits->first()->invitations->first(); | ||||
|         $path = $this->credits->first()->client->quote_filepath($invitation); | ||||
|         $file_name = now()->addSeconds($this->company->timezone_offset())->format('Y-m-d-h-m-s').'_'.str_replace(' ', '_', trans('texts.credits')).'.zip'; | ||||
| 
 | ||||
|         $this->credits->each(function ($credit) { | ||||
|             (new CreateEntityPdf($credit->invitations()->first()))->handle(); | ||||
|         }); | ||||
|         $invitations = CreditInvitation::query()->with('credit')->whereIn('credit_id', $this->credit_ids)->get(); | ||||
|         $invitation = $invitations->first(); | ||||
|         $path = $invitation->contact->client->credit_filepath($invitation); | ||||
| 
 | ||||
|         try { | ||||
|             foreach ($this->credits as $credit) { | ||||
|                 $file = $credit->service()->getCreditPdf($credit->invitations()->first()); | ||||
|                 $zip_file_name = basename($file); | ||||
|                 $zipFile->addFromString($zip_file_name, Storage::get($file)); | ||||
|             foreach ($invitations as $invitation) { | ||||
|                 $file = (new \App\Jobs\Entity\CreateRawPdf($invitation, $this->company->db))->handle(); | ||||
|                 $zipFile->addFromString($invitation->credit->numberFormatter() . '.pdf', $file); | ||||
|             } | ||||
| 
 | ||||
|             Storage::put($path.$file_name, $zipFile->outputAsString()); | ||||
|  | ||||
| @ -11,12 +11,14 @@ | ||||
| 
 | ||||
| namespace App\Jobs\Cron; | ||||
| 
 | ||||
| use App\Utils\Ninja; | ||||
| use App\Libraries\MultiDB; | ||||
| use Illuminate\Support\Carbon; | ||||
| use App\Models\RecurringExpense; | ||||
| use App\Models\RecurringInvoice; | ||||
| use Illuminate\Support\Facades\Auth; | ||||
| use App\Utils\Traits\GeneratesCounter; | ||||
| use App\Events\Expense\ExpenseWasCreated; | ||||
| use Illuminate\Foundation\Bus\Dispatchable; | ||||
| use App\Factory\RecurringExpenseToExpenseFactory; | ||||
| 
 | ||||
| @ -109,6 +111,9 @@ class RecurringExpensesCron | ||||
|         $expense->number = $this->getNextExpenseNumber($expense); | ||||
|         $expense->saveQuietly(); | ||||
| 
 | ||||
|         event(new ExpenseWasCreated($expense, $expense->company, Ninja::eventVars(null))); | ||||
|         event('eloquent.created: App\Models\Expense', $expense); | ||||
| 
 | ||||
|         $recurring_expense->next_send_date = $recurring_expense->nextSendDate(); | ||||
|         $recurring_expense->next_send_date_client = $recurring_expense->next_send_date; | ||||
|         $recurring_expense->last_sent_date = now(); | ||||
|  | ||||
| @ -80,8 +80,8 @@ class UpdateCalculatedFields | ||||
| 
 | ||||
|         $project->tasks->each(function ($task) use (&$duration) { | ||||
|              | ||||
|          | ||||
|             foreach(json_decode($task->time_log) as $log){ | ||||
|         if(is_iterable($task->time_log)) { | ||||
|             foreach(json_decode($task->time_log) as $log) { | ||||
| 
 | ||||
|                 $start_time = $log[0]; | ||||
|                 $end_time = $log[1] == 0 ? time() : $log[1]; | ||||
| @ -89,6 +89,7 @@ class UpdateCalculatedFields | ||||
|                 $duration += $end_time - $start_time; | ||||
| 
 | ||||
|             } | ||||
|         } | ||||
|                      | ||||
|         }); | ||||
| 
 | ||||
|  | ||||
| @ -220,7 +220,7 @@ class CreateRawPdf implements ShouldQueue | ||||
|      */ | ||||
|     private function checkEInvoice(string $pdf): string | ||||
|     { | ||||
|         if(!$this->entity instanceof Invoice) | ||||
|         if(!$this->entity instanceof Invoice || !$this->company->getSetting('enable_e_invoice')) | ||||
|             return $pdf; | ||||
| 
 | ||||
|         $e_invoice_type = $this->entity->client->getSetting('e_invoice_type'); | ||||
|  | ||||
							
								
								
									
										79
									
								
								app/Jobs/Ninja/CheckACHStatus.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								app/Jobs/Ninja/CheckACHStatus.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Invoice Ninja (https://invoiceninja.com). | ||||
|  * | ||||
|  * @link https://github.com/invoiceninja/invoiceninja source repository | ||||
|  * | ||||
|  * @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com) | ||||
|  * | ||||
|  * @license https://www.elastic.co/licensing/elastic-license | ||||
|  */ | ||||
| 
 | ||||
| namespace App\Jobs\Ninja; | ||||
| 
 | ||||
| use App\Libraries\MultiDB; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use App\Models\ClientGatewayToken; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| use Illuminate\Queue\InteractsWithQueue; | ||||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| use Illuminate\Foundation\Bus\Dispatchable; | ||||
| 
 | ||||
| class CheckACHStatus implements ShouldQueue | ||||
| { | ||||
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new job instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
|         //
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Execute the job. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         //multiDB environment, need to
 | ||||
|         foreach (MultiDB::$dbs as $db) { | ||||
|             MultiDB::setDB($db); | ||||
| 
 | ||||
|             nlog("Checking ACH status"); | ||||
| 
 | ||||
|             ClientGatewayToken::query() | ||||
|             ->where('created_at', '>', now()->subMonths(2)) | ||||
|             ->where('gateway_type_id', 2) | ||||
|             ->whereHas('gateway', function ($q) { | ||||
|                 $q->whereIn('gateway_key', ['d14dd26a37cecc30fdd65700bfb55b23','d14dd26a47cecc30fdd65700bfb67b34']); | ||||
|             }) | ||||
|             ->whereJsonContains('meta', ['state' => 'unauthorized']) | ||||
|             ->cursor() | ||||
|             ->each(function ($token) { | ||||
| 
 | ||||
|                 try { | ||||
|                     $stripe = $token->gateway->driver($token->client)->init(); | ||||
|                     $pm =  $stripe->getStripePaymentMethod($token->token); | ||||
| 
 | ||||
|                     if($pm) { | ||||
| 
 | ||||
|                         $meta = $token->meta; | ||||
|                         $meta->state = 'authorized'; | ||||
|                         $token->meta = $meta; | ||||
|                         $token->save(); | ||||
| 
 | ||||
|                     } | ||||
| 
 | ||||
|                 } catch (\Exception $e) { | ||||
|                 } | ||||
| 
 | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -18,6 +18,7 @@ use App\Jobs\Vendor\CreatePurchaseOrderPdf; | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Mail\DownloadPurchaseOrders; | ||||
| use App\Models\Company; | ||||
| use App\Models\PurchaseOrderInvitation; | ||||
| use App\Models\User; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| @ -30,32 +31,12 @@ class ZipPurchaseOrders implements ShouldQueue | ||||
| { | ||||
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||||
| 
 | ||||
|     public $purchase_orders; | ||||
| 
 | ||||
|     private $company; | ||||
| 
 | ||||
|     private $user; | ||||
| 
 | ||||
|     public $settings; | ||||
| 
 | ||||
|     public $tries = 1; | ||||
| 
 | ||||
|     /** | ||||
|      * @param $purchase_orders | ||||
|      * @param Company $company | ||||
|      * @param $email | ||||
|      * @deprecated confirm to be deleted | ||||
|      * Create a new job instance. | ||||
|      */ | ||||
|     public function __construct($purchase_orders, Company $company, User $user) | ||||
|     public function __construct(protected array $purchase_order_ids, protected Company $company, protected User $user) | ||||
|     { | ||||
|         $this->purchase_orders = $purchase_orders; | ||||
| 
 | ||||
|         $this->company = $company; | ||||
| 
 | ||||
|         $this->user = $user; | ||||
| 
 | ||||
|         $this->settings = $company->settings; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -67,21 +48,23 @@ class ZipPurchaseOrders implements ShouldQueue | ||||
|     { | ||||
|         MultiDB::setDb($this->company->db); | ||||
| 
 | ||||
|         $this->settings = $this->company->settings; | ||||
| 
 | ||||
|         // create new zip object
 | ||||
|         $zipFile = new \PhpZip\ZipFile(); | ||||
|         $file_name = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.purchase_orders')).'.zip'; | ||||
|         $invitation = $this->purchase_orders->first()->invitations->first(); | ||||
|         $path = $this->purchase_orders->first()->vendor->purchase_order_filepath($invitation); | ||||
|         $file_name = now()->addSeconds($this->company->timezone_offset())->format('Y-m-d-h-m-s').'_'.str_replace(' ', '_', trans('texts.purchase_orders')).'.zip'; | ||||
| 
 | ||||
|         $this->purchase_orders->each(function ($purchase_order) { | ||||
|             (new CreatePurchaseOrderPdf($purchase_order->invitations()->first()))->handle(); | ||||
|         }); | ||||
|         $invitations = PurchaseOrderInvitation::query() | ||||
|                                             ->with('purchase_order') | ||||
|                                             ->whereIn('purchase_order_id', $this->purchase_order_ids) | ||||
|                                             ->get(); | ||||
|         $invitation = $invitations->first(); | ||||
|         $path = $invitation->contact->vendor->purchase_order_filepath($invitation); | ||||
| 
 | ||||
|         try { | ||||
|             foreach ($this->purchase_orders as $purchase_order) { | ||||
|                 $file = $purchase_order->service()->getPurchaseOrderPdf(); | ||||
|                 $zip_file_name = basename($file); | ||||
|                 $zipFile->addFromString($zip_file_name, Storage::get($file)); | ||||
|             foreach ($invitations as $invitation) { | ||||
|                 $file = (new \App\Jobs\Vendor\CreatePurchaseOrderPdf($invitation))->rawPdf(); | ||||
|                 $zipFile->addFromString($invitation->purchase_order->numberFormatter().".pdf", $file); | ||||
|             } | ||||
| 
 | ||||
|             Storage::put($path.$file_name, $zipFile->outputAsString()); | ||||
|  | ||||
| @ -11,51 +11,31 @@ | ||||
| 
 | ||||
| namespace App\Jobs\Quote; | ||||
| 
 | ||||
| use App\Jobs\Entity\CreateEntityPdf; | ||||
| use App\Jobs\Mail\NinjaMailerJob; | ||||
| use App\Jobs\Mail\NinjaMailerObject; | ||||
| use App\Jobs\Util\UnlinkFile; | ||||
| use App\Models\User; | ||||
| use App\Models\Company; | ||||
| use App\Libraries\MultiDB; | ||||
| use App\Mail\DownloadQuotes; | ||||
| use App\Models\Company; | ||||
| use App\Models\User; | ||||
| use App\Jobs\Util\UnlinkFile; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| use Illuminate\Foundation\Bus\Dispatchable; | ||||
| use Illuminate\Queue\InteractsWithQueue; | ||||
| use App\Models\QuoteInvitation; | ||||
| use App\Jobs\Mail\NinjaMailerJob; | ||||
| use App\Jobs\Mail\NinjaMailerObject; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| use Illuminate\Support\Facades\Storage; | ||||
| use Illuminate\Queue\InteractsWithQueue; | ||||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| use Illuminate\Foundation\Bus\Dispatchable; | ||||
| 
 | ||||
| class ZipQuotes implements ShouldQueue | ||||
| { | ||||
|     use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | ||||
| 
 | ||||
|     public $quotes; | ||||
| 
 | ||||
|     private $company; | ||||
| 
 | ||||
|     private $user; | ||||
| 
 | ||||
|     public $settings; | ||||
| 
 | ||||
|     public $tries = 1; | ||||
| 
 | ||||
|     /** | ||||
|      * @param $invoices | ||||
|      * @param Company $company | ||||
|      * @param $email | ||||
|      * @deprecated confirm to be deleted | ||||
|      * Create a new job instance. | ||||
|      */ | ||||
|     public function __construct($quotes, Company $company, User $user) | ||||
|     public function __construct(protected array $quote_ids, protected Company $company, protected User $user) | ||||
|     { | ||||
|         $this->quotes = $quotes; | ||||
| 
 | ||||
|         $this->company = $company; | ||||
| 
 | ||||
|         $this->user = $user; | ||||
| 
 | ||||
|         $this->settings = $company->settings; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -66,27 +46,22 @@ class ZipQuotes implements ShouldQueue | ||||
|     public function handle() | ||||
|     { | ||||
|         MultiDB::setDb($this->company->db); | ||||
|          | ||||
|         $this->settings = $this->company->settings; | ||||
| 
 | ||||
|         // create new zip object
 | ||||
|         $zipFile = new \PhpZip\ZipFile(); | ||||
|         $file_name = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.quotes')).'.zip'; | ||||
|         $invitation = $this->quotes->first()->invitations->first(); | ||||
|         $path = $this->quotes->first()->client->quote_filepath($invitation); | ||||
|         $file_name = now()->addSeconds($this->company->timezone_offset())->format('Y-m-d-h-m-s').'_'.str_replace(' ', '_', trans('texts.quotes')).'.zip'; | ||||
| 
 | ||||
|         $this->quotes->each(function ($quote) { | ||||
|             $quote->service()->createInvitations(); | ||||
| 
 | ||||
|             (new CreateEntityPdf($quote->invitations()->first()))->handle(); | ||||
|         }); | ||||
|         $invitations = QuoteInvitation::query()->with('quote')->whereIn('quote_id', $this->quote_ids)->get(); | ||||
|         $invitation = $invitations->first(); | ||||
|         $path = $invitation->contact->client->quote_filepath($invitation); | ||||
| 
 | ||||
|         try { | ||||
|             foreach ($this->quotes as $quote) { | ||||
|                 $file = $quote->service()->getQuotePdf(); | ||||
|                 $zip_file_name = basename($file); | ||||
|                 $zipFile->addFromString($zip_file_name, Storage::get($file)); | ||||
| 
 | ||||
|                 // $download_file = file_get_contents($quote->pdf_file_path($invitation, 'url', true));
 | ||||
|                 // $zipFile->addFromString(basename($quote->pdf_file_path($invitation)), $download_file);
 | ||||
|             foreach ($invitations as $invitation) { | ||||
|                 $file = (new \App\Jobs\Entity\CreateRawPdf($invitation, $this->company->db))->handle(); | ||||
|                 $zipFile->addFromString($invitation->quote->numberFormatter() . '.pdf', $file); | ||||
|             } | ||||
| 
 | ||||
|             Storage::put($path.$file_name, $zipFile->outputAsString()); | ||||
| @ -100,8 +75,9 @@ class ZipQuotes implements ShouldQueue | ||||
|             NinjaMailerJob::dispatch($nmo); | ||||
| 
 | ||||
|             UnlinkFile::dispatch(config('filesystems.default'), $path.$file_name)->delay(now()->addHours(1)); | ||||
| 
 | ||||
|         } catch (\PhpZip\Exception\ZipException $e) { | ||||
|             // handle exception
 | ||||
|             nlog("zip build failed: ".$e->getMessage()); | ||||
|         } finally { | ||||
|             $zipFile->close(); | ||||
|         } | ||||
|  | ||||
| @ -11,22 +11,24 @@ | ||||
| 
 | ||||
| namespace App\Jobs\RecurringInvoice; | ||||
| 
 | ||||
| use App\DataMapper\Analytics\SendRecurringFailure; | ||||
| use App\Factory\InvoiceInvitationFactory; | ||||
| use App\Factory\RecurringInvoiceToInvoiceFactory; | ||||
| use App\Jobs\Cron\AutoBill; | ||||
| use App\Jobs\Entity\EmailEntity; | ||||
| use Carbon\Carbon; | ||||
| use App\Utils\Ninja; | ||||
| use App\Models\Invoice; | ||||
| use App\Jobs\Cron\AutoBill; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use App\Jobs\Entity\EmailEntity; | ||||
| use App\Models\RecurringInvoice; | ||||
| use App\Utils\Traits\GeneratesCounter; | ||||
| use App\Utils\Traits\MakesHash; | ||||
| use Carbon\Carbon; | ||||
| use Illuminate\Bus\Queueable; | ||||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| use Illuminate\Foundation\Bus\Dispatchable; | ||||
| use Illuminate\Queue\InteractsWithQueue; | ||||
| use Illuminate\Queue\SerializesModels; | ||||
| use Turbo124\Beacon\Facades\LightLogs; | ||||
| use Illuminate\Queue\InteractsWithQueue; | ||||
| use App\Events\Invoice\InvoiceWasCreated; | ||||
| use App\Factory\InvoiceInvitationFactory; | ||||
| use Illuminate\Contracts\Queue\ShouldQueue; | ||||
| use Illuminate\Foundation\Bus\Dispatchable; | ||||
| use App\Factory\RecurringInvoiceToInvoiceFactory; | ||||
| use App\DataMapper\Analytics\SendRecurringFailure; | ||||
| 
 | ||||
| class SendRecurring implements ShouldQueue | ||||
| { | ||||
| @ -105,6 +107,7 @@ class SendRecurring implements ShouldQueue | ||||
|         $this->recurring_invoice->save(); | ||||
| 
 | ||||
|         event('eloquent.created: App\Models\Invoice', $invoice); | ||||
|         event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars())); | ||||
| 
 | ||||
|         //auto bill, BUT NOT DRAFTS!!
 | ||||
|         if ($invoice->auto_bill_enabled && $invoice->client->getSetting('auto_bill_date') == 'on_send_date' && $invoice->client->getSetting('auto_email_invoice')) { | ||||
|  | ||||
| @ -39,8 +39,8 @@ class PreviewReport implements ShouldQueue | ||||
|         /** @var \App\Export\CSV\CreditExport $export */ | ||||
|         $export = new $this->report_class($this->company, $this->request); | ||||
|         $report = $export->returnJson(); | ||||
|         nlog($report); | ||||
|         nlog($this->report_class); | ||||
|         // nlog($report);
 | ||||
|         // nlog($this->report_class);
 | ||||
|         // nlog($report);
 | ||||
|         Cache::put($this->hash, $report, 60 * 60); | ||||
|     } | ||||
|  | ||||
| @ -76,7 +76,7 @@ class CreateUser | ||||
|             'is_admin' => 1, | ||||
|             'is_locked' => 0, | ||||
|             'permissions' => '', | ||||
|             'notifications' => CompanySettings::notificationDefaults(), | ||||
|             'notifications' => CompanySettings::notificationAdminDefaults(), | ||||
|             'settings' => null, | ||||
|         ]); | ||||
| 
 | ||||
|  | ||||
| @ -49,7 +49,8 @@ class CreatedExpenseActivity implements ShouldQueue | ||||
|         $fields->user_id = $user_id; | ||||
|         $fields->company_id = $event->expense->company_id; | ||||
|         $fields->activity_type_id = Activity::CREATE_EXPENSE; | ||||
| 
 | ||||
|         $fields->recurring_expense_id = $event->expense->recurring_expense_id ?? null; | ||||
|          | ||||
|         $this->activity_repo->save($fields, $event->expense, $event->event_vars); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -52,7 +52,8 @@ class CreateInvoiceActivity implements ShouldQueue | ||||
|         $fields->client_id = $event->invoice->client_id; | ||||
|         $fields->company_id = $event->invoice->company_id; | ||||
|         $fields->activity_type_id = Activity::CREATE_INVOICE; | ||||
| 
 | ||||
|         $fields->recurring_invoice_id = $event->invoice->recurring_id; | ||||
|          | ||||
|         $this->activity_repo->save($fields, $event->invoice, $event->event_vars); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -73,7 +73,7 @@ class EntitySentObject | ||||
|             ); | ||||
|             $mail_obj->data = [ | ||||
|                 'title' => $mail_obj->subject, | ||||
|                 'message' => ctrans( | ||||
|                 'content' => ctrans( | ||||
|                     $this->template_body, | ||||
|                     [ | ||||
|                         'amount' => $mail_obj->amount, | ||||
| @ -98,7 +98,7 @@ class EntitySentObject | ||||
|             $mail_obj->markdown = 'email.admin.generic'; | ||||
|             $mail_obj->tag = $this->company->company_key; | ||||
|         } | ||||
|          | ||||
|         // nlog($mail_obj);
 | ||||
|         return $mail_obj; | ||||
|     } | ||||
| 
 | ||||
| @ -186,7 +186,7 @@ class EntitySentObject | ||||
| 
 | ||||
|         return [ | ||||
|             'title' => $this->getSubject(), | ||||
|             'message' => $this->getMessage(), | ||||
|             'content' => $this->getMessage(), | ||||
|             'url' => $this->invitation->getAdminLink($this->use_react_url), | ||||
|             'button' => ctrans("texts.view_{$this->entity_type}"), | ||||
|             'signature' => $settings->email_signature, | ||||
|  | ||||
| @ -114,11 +114,6 @@ class CreditEmailEngine extends BaseEmailEngine | ||||
|             ->setTextBody($text_body); | ||||
| 
 | ||||
|         if ($this->client->getSetting('pdf_email_attachment') !== false && $this->credit->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { | ||||
|             // if (Ninja::isHosted()) {
 | ||||
|             //     $this->setAttachments([$this->credit->pdf_file_path($this->invitation, 'url', true)]);
 | ||||
|             // } else {
 | ||||
|             //     $this->setAttachments([$this->credit->pdf_file_path($this->invitation)]);
 | ||||
|             // }
 | ||||
| 
 | ||||
|             $pdf = ((new CreateRawPdf($this->invitation, $this->invitation->company->db))->handle()); | ||||
| 
 | ||||
| @ -128,21 +123,21 @@ class CreditEmailEngine extends BaseEmailEngine | ||||
|         //attach third party documents
 | ||||
|         if ($this->client->getSetting('document_email_attachment') !== false && $this->credit->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) { | ||||
|             // Storage::url
 | ||||
|             foreach ($this->credit->documents as $document) { | ||||
|             $this->credit->documents()->where('is_public',true)->cursor()->each(function($document) { | ||||
|                 if ($document->size > $this->max_attachment_size) { | ||||
|                     $this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]); | ||||
|                 } else { | ||||
|                     $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null, 'file' => base64_encode($document->getFile())]]); | ||||
|                 } | ||||
|             } | ||||
|             }); | ||||
| 
 | ||||
|             foreach ($this->credit->company->documents as $document) { | ||||
|             $this->credit->company->documents()->where('is_public',true)->cursor()->each(function($document) { | ||||
|                 if ($document->size > $this->max_attachment_size) { | ||||
|                     $this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]); | ||||
|                 } else { | ||||
|                     $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null, 'file' => base64_encode($document->getFile())]]); | ||||
|                 } | ||||
|             } | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|  | ||||
| @ -135,31 +135,31 @@ class InvoiceEmailEngine extends BaseEmailEngine | ||||
|         //attach third party documents
 | ||||
|         if ($this->client->getSetting('document_email_attachment') !== false && $this->invoice->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) { | ||||
|             if ($this->invoice->recurring_invoice()->exists()) { | ||||
|                 foreach ($this->invoice->recurring_invoice->documents as $document) { | ||||
|                 $this->invoice->recurring_invoice->documents()->where('is_public',true)->cursor()->each(function ($document) { | ||||
|                     if ($document->size > $this->max_attachment_size) { | ||||
|                         $this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]); | ||||
|                     } else { | ||||
|                         $this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => null, ]]); | ||||
|                     } | ||||
|                 } | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|             // Storage::url
 | ||||
|             foreach ($this->invoice->documents as $document) { | ||||
|             $this->invoice->documents()->where('is_public',true)->cursor()->each(function ($document) { | ||||
|                 if ($document->size > $this->max_attachment_size) { | ||||
|                     $this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]); | ||||
|                 } else { | ||||
|                     $this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => null, ]]); | ||||
|                 } | ||||
|             } | ||||
|             }); | ||||
| 
 | ||||
|             foreach ($this->invoice->company->documents as $document) { | ||||
|             $this->invoice->company->documents()->where('is_public',true)->cursor()->each(function ($document) { | ||||
|                 if ($document->size > $this->max_attachment_size) { | ||||
|                     $this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]); | ||||
|                 } else { | ||||
|                     $this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => null, ]]); | ||||
|                 } | ||||
|             } | ||||
|             }); | ||||
| 
 | ||||
|             $line_items = $this->invoice->line_items; | ||||
| 
 | ||||
| @ -175,13 +175,13 @@ class InvoiceEmailEngine extends BaseEmailEngine | ||||
|                                        ->where('invoice_documents', 1) | ||||
|                                        ->cursor() | ||||
|                                        ->each(function ($expense) { | ||||
|                                            foreach ($expense->documents as $document) { | ||||
|                                            $expense->documents()->where('is_public',true)->cursor()->each(function ($document) { | ||||
|                                                if ($document->size > $this->max_attachment_size) { | ||||
|                                                    $this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]); | ||||
|                                                } else { | ||||
|                                                    $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null, ]]); | ||||
|                                                } | ||||
|                                            } | ||||
|                                            }); | ||||
|                                        }); | ||||
|                 } | ||||
| 
 | ||||
| @ -195,13 +195,13 @@ class InvoiceEmailEngine extends BaseEmailEngine | ||||
|                     $tasks = Task::query()->whereIn('id', $this->transformKeys($task_ids)) | ||||
|                                        ->cursor() | ||||
|                                        ->each(function ($task) { | ||||
|                                            foreach ($task->documents as $document) { | ||||
|                                            $task->documents()->where('is_public', true)->cursor()->each(function ($document) { | ||||
|                                                if ($document->size > $this->max_attachment_size) { | ||||
|                                                    $this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]); | ||||
|                                                } else { | ||||
|                                                    $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null, ]]); | ||||
|                                                } | ||||
|                                            } | ||||
|                                            }); | ||||
|                                        }); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
| @ -98,13 +98,13 @@ class PaymentEmailEngine extends BaseEmailEngine | ||||
| 
 | ||||
|                 //attach invoice documents also to payments
 | ||||
|                 if ($this->client->getSetting('document_email_attachment') !== false) { | ||||
|                     foreach ($invoice->documents as $document) { | ||||
|                     $invoice->documents()->where('is_public', true)->cursor()->each(function ($document){ | ||||
|                         if ($document->size > $this->max_attachment_size) { | ||||
|                             $this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]); | ||||
|                         } else { | ||||
|                             $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null, ]]); | ||||
|                         } | ||||
|                     } | ||||
|                     }); | ||||
|                 } | ||||
| 
 | ||||
|                 if($this->client->getSetting('enable_e_invoice')) | ||||
|  | ||||
| @ -127,21 +127,21 @@ class PurchaseOrderEmailEngine extends BaseEmailEngine | ||||
|         //attach third party documents
 | ||||
|         if ($this->vendor->getSetting('document_email_attachment') !== false && $this->purchase_order->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) { | ||||
|             // Storage::url
 | ||||
|             foreach ($this->purchase_order->documents as $document) { | ||||
|             $this->purchase_order->documents()->where('is_public', true)->cursor()->each(function ($document) { | ||||
|                 if ($document->size > $this->max_attachment_size) { | ||||
|                     $this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]); | ||||
|                 } else { | ||||
|                     $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]); | ||||
|                 } | ||||
|             } | ||||
|             }); | ||||
| 
 | ||||
|             foreach ($this->purchase_order->company->documents as $document) { | ||||
|             $this->purchase_order->company->documents()->where('is_public', true)->cursor()->each(function ($document) { | ||||
|                 if ($document->size > $this->max_attachment_size) { | ||||
|                     $this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]); | ||||
|                 } else { | ||||
|                     $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]); | ||||
|                 } | ||||
|             } | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|  | ||||
| @ -121,21 +121,21 @@ class QuoteEmailEngine extends BaseEmailEngine | ||||
|         //attach third party documents
 | ||||
|         if ($this->client->getSetting('document_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_DOCUMENTS)) { | ||||
|             // Storage::url
 | ||||
|             foreach ($this->quote->documents as $document) { | ||||
|             $this->quote->documents()->where('is_public', true)->cursor()->each(function ($document) { | ||||
|                 if ($document->size > $this->max_attachment_size) { | ||||
|                     $this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]); | ||||
|                 } else { | ||||
|                     $this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => null, ]]); | ||||
|                 } | ||||
|             } | ||||
|             }); | ||||
| 
 | ||||
|             foreach ($this->quote->company->documents as $document) { | ||||
|             $this->quote->company->documents()->where('is_public', true)->cursor()->each(function ($document) { | ||||
|                 if ($document->size > $this->max_attachment_size) { | ||||
|                     $this->setAttachmentLinks(["<a class='doc_links' href='" . URL::signedRoute('documents.public_download', ['document_hash' => $document->hash]) ."'>". $document->name ."</a>"]); | ||||
|                 } else { | ||||
|                     $this->setAttachments([['file' => base64_encode($document->getFile()), 'path' => $document->filePath(), 'name' => $document->name, 'mime' => null, ]]); | ||||
|                 } | ||||
|             } | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|  | ||||
| @ -416,6 +416,9 @@ class Activity extends StaticModel | ||||
|         if($this->vendor) | ||||
|             $replacements['vendor'] = ['label' => $this?->vendor?->present()->name() ?? '', 'hashed_id' => $this->vendor->hashed_id ?? '']; | ||||
| 
 | ||||
|         if($this->activity_type_id == 4 && $this->recurring_invoice) | ||||
|             $replacements['recurring_invoice'] = ['label' => $this?->recurring_invoice?->number ?? '', 'hashed_id' => $this->recurring_invoice->hashed_id ?? '']; | ||||
| 
 | ||||
|         $replacements['activity_type_id'] = $this->activity_type_id; | ||||
|         $replacements['id'] = $this->id; | ||||
|         $replacements['hashed_id'] = $this->hashed_id; | ||||
|  | ||||
| @ -105,22 +105,22 @@ class BaseModel extends Model | ||||
|         return $value; | ||||
|     } | ||||
| 
 | ||||
|     public function __call($method, $params) | ||||
|     { | ||||
|         $entity = strtolower(class_basename($this)); | ||||
|     // public function __call($method, $params)
 | ||||
|     // {
 | ||||
|     //     $entity = strtolower(class_basename($this));
 | ||||
| 
 | ||||
|         if ($entity) { | ||||
|             $configPath = "modules.relations.$entity.$method"; | ||||
|     //     if ($entity) {
 | ||||
|     //         $configPath = "modules.relations.$entity.$method";
 | ||||
| 
 | ||||
|             if (config()->has($configPath)) { | ||||
|                 $function = config()->get($configPath); | ||||
|     //         if (config()->has($configPath)) {
 | ||||
|     //             $function = config()->get($configPath);
 | ||||
| 
 | ||||
|                 return call_user_func_array([$this, $function[0]], $function[1]); | ||||
|             } | ||||
|         } | ||||
|     //             return call_user_func_array([$this, $function[0]], $function[1]);
 | ||||
|     //         }
 | ||||
|     //     }
 | ||||
| 
 | ||||
|         return parent::__call($method, $params); | ||||
|     } | ||||
|     //     return parent::__call($method, $params);
 | ||||
|     // }
 | ||||
| 
 | ||||
|     /** | ||||
|     * @param  \Illuminate\Database\Eloquent\Builder  $query | ||||
|  | ||||
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