merge v5-develop

This commit is contained in:
David Bomba 2023-09-24 12:10:43 +10:00
commit da54eea655
654 changed files with 303009 additions and 290940 deletions

View File

@ -14,6 +14,11 @@ https://invoiceninja.github.io/docs/self-host-troubleshooting/ -->
- Version: <!-- i.e. v4.5.25 / v5.0.30 -->
- Environment: <!-- Docker/Shared Hosting/ZIP/Other -->
## Interface
- Flutter: []
- React: []
- Both: []
## Checklist
- Can you replicate the issue on our v5 demo site https://demo.invoiceninja.com or https://react.invoicing.co/demo?
- Have you searched existing issues?

View File

@ -32,9 +32,9 @@ jobs:
- name: Prepare Laravel Application
run: |
cp .env.example .env
php artisan key:generate
php artisan key:generate --force
php artisan optimize
php artisan storage:link
php artisan storage:link --force
sudo php artisan cache:clear
sudo find ./vendor/bin/ -type f -exec chmod +x {} \;
sudo find ./ -type d -exec chmod 755 {} \;
@ -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 ..

View File

@ -1 +1 @@
5.6.26
5.7.22

View File

@ -35,6 +35,7 @@ use App\Models\BankTransaction;
use App\Models\QuoteInvitation;
use Illuminate\Console\Command;
use App\Models\CreditInvitation;
use App\Models\RecurringInvoice;
use App\Models\InvoiceInvitation;
use App\DataMapper\ClientSettings;
use Illuminate\Support\Facades\DB;
@ -185,6 +186,9 @@ class CheckData extends Command
if ($cu->company && $cu->user) {
(new CreateCompanyToken($cu->company, $cu->user, 'System'))->handle();
}
else {
// $cu->forceDelete();
}
}
});
}
@ -468,7 +472,7 @@ class CheckData extends Command
$ii->saveQuietly();
});
collect([Invoice::class, Quote::class, Credit::class, PurchaseOrder::class])->each(function ($entity) {
collect([Invoice::class, Quote::class, Credit::class, PurchaseOrder::class, RecurringInvoice::class])->each(function ($entity) {
if ($entity::doesntHave('invitations')->count() > 0) {
$entity::doesntHave('invitations')->cursor()->each(function ($entity) {
$client_vendor_key = 'client_id';
@ -545,7 +549,7 @@ class CheckData extends Command
private function clientPaidToDateQuery()
{
$results = \DB::select(\DB::raw("
$results = \DB::select("
SELECT
clients.id as client_id,
clients.paid_to_date as client_paid_to_date,
@ -560,14 +564,14 @@ class CheckData extends Command
GROUP BY clients.id
HAVING payments_applied != client_paid_to_date
ORDER BY clients.id;
"));
");
return $results;
}
private function clientCreditPaymentables($client)
{
$results = \DB::select(\DB::raw("
$results = \DB::select("
SELECT
SUM(paymentables.amount - paymentables.refunded) as credit_payment
FROM payments
@ -579,7 +583,7 @@ class CheckData extends Command
AND paymentables.amount > 0
AND payments.is_deleted = 0
AND payments.client_id = ?;
"), [App\Models\Credit::class, $client->id]);
", [App\Models\Credit::class, $client->id]);
return $results;
}
@ -617,108 +621,9 @@ class CheckData extends Command
$this->logMessage("{$this->wrong_paid_to_dates} clients with incorrect paid to dates");
}
private function checkPaidToDates()
{
$this->wrong_paid_to_dates = 0;
$credit_total_applied = 0;
$clients = DB::table('clients')
->leftJoin('payments', function ($join) {
$join->on('payments.client_id', '=', 'clients.id')
->where('payments.is_deleted', 0)
->whereIn('payments.status_id', [Payment::STATUS_COMPLETED, Payment::STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED]);
})
->where('clients.is_deleted', 0)
->where('clients.updated_at', '>', now()->subDays(2))
->groupBy('clients.id')
->havingRaw('clients.paid_to_date != sum(coalesce(payments.amount - payments.refunded, 0))')
->get(['clients.id', 'clients.paid_to_date', DB::raw('sum(coalesce(payments.amount - payments.refunded, 0)) as amount')]);
/* Due to accounting differences we need to perform a second loop here to ensure there actually is an issue */
$clients->each(function ($client_record) use ($credit_total_applied) {
$client = Client::withTrashed()->find($client_record->id);
$total_invoice_payments = 0;
foreach ($client->invoices()->where('is_deleted', false)->where('status_id', '>', 1)->get() as $invoice) {
$total_invoice_payments += $invoice->payments()
->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment::STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])
->selectRaw('sum(paymentables.amount - paymentables.refunded) as p')
->pluck('p')
->first();
}
//commented IN 27/06/2021 - sums ALL client payments AND the unapplied amounts to match the client paid to date
$p = Payment::where('client_id', $client->id)
->where('is_deleted', 0)
->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment::STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])
->sum(DB::Raw('amount - applied'));
$total_invoice_payments += $p;
// 10/02/21
foreach ($client->payments as $payment) {
$credit_total_applied += $payment->paymentables()
->where('paymentable_type', App\Models\Credit::class)
->selectRaw('sum(paymentables.amount - paymentables.refunded) as p')
->pluck('p')
->first();
}
if ($credit_total_applied < 0) {
$total_invoice_payments += $credit_total_applied;
}
if (round($total_invoice_payments, 2) != round($client->paid_to_date, 2)) {
$this->wrong_paid_to_dates++;
$this->logMessage($client->present()->name().' id = # '.$client->id." - Paid to date does not match Client Paid To Date = {$client->paid_to_date} - Invoice Payments = {$total_invoice_payments}");
$this->isValid = false;
if ($this->option('paid_to_date')) {
$this->logMessage("# {$client->id} " . $client->present()->name().' - '.$client->number." Fixing {$client->paid_to_date} to {$total_invoice_payments}");
$client->paid_to_date = $total_invoice_payments;
$client->save();
}
}
});
$this->logMessage("{$this->wrong_paid_to_dates} clients with incorrect paid to dates");
}
private function checkInvoicePayments()
{
$this->wrong_balances = 0;
Client::cursor()->where('is_deleted', 0)->where('clients.updated_at', '>', now()->subDays(2))->each(function ($client) {
$client->invoices->where('is_deleted', false)->whereIn('status_id', '!=', Invoice::STATUS_DRAFT)->each(function ($invoice) use ($client) {
$total_paid = $invoice->payments()
->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment::STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])
->selectRaw('sum(paymentables.amount - paymentables.refunded) as p')
->pluck('p')
->first();
$total_credit = $invoice->credits()->get()->sum('amount');
$calculated_paid_amount = $invoice->amount - $invoice->balance - $total_credit;
if ((string)$total_paid != (string)($invoice->amount - $invoice->balance - $total_credit)) {
$this->wrong_balances++;
$this->logMessage($client->present()->name().' - '.$client->id." - Total Paid = {$total_paid} != Calculated Total = {$calculated_paid_amount}");
$this->isValid = false;
}
});
});
$this->logMessage("{$this->wrong_balances} clients with incorrect invoice balances");
}
private function clientBalanceQuery()
{
$results = \DB::select(\DB::raw("
$results = \DB::select("
SELECT
SUM(invoices.balance) as invoice_balance,
clients.id as client_id,
@ -732,7 +637,7 @@ class CheckData extends Command
GROUP BY clients.id
HAVING invoice_balance != clients.balance
ORDER BY clients.id;
"));
");
return $results;
}
@ -809,7 +714,7 @@ class CheckData extends Command
private function invoiceBalanceQuery()
{
$results = \DB::select(\DB::raw("
$results = \DB::select("
SELECT
clients.id,
clients.balance,
@ -823,7 +728,7 @@ class CheckData extends Command
GROUP BY clients.id
HAVING(invoices_balance != clients.balance)
ORDER BY clients.id;
"));
");
return $results;
}
@ -873,7 +778,7 @@ class CheckData extends Command
$this->wrong_balances = 0;
$this->wrong_paid_to_dates = 0;
foreach (Client::where('is_deleted', 0)->where('clients.updated_at', '>', now()->subDays(2))->cursor() as $client) {
foreach (Client::query()->where('is_deleted', 0)->where('clients.updated_at', '>', now()->subDays(2))->cursor() as $client) {
$invoice_balance = $client->invoices()->where('is_deleted', false)->whereIn('status_id', [2,3])->sum('balance');
$ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();
@ -957,7 +862,7 @@ class CheckData extends Command
}
$records = DB::table($table)
->join($tableName, "{$tableName}.id", '=', "{$table}.{$field}_id")
->where("{$table}.{$company_id}", '!=', DB::raw("{$tableName}.company_id"))
->whereRaw("{$table}.{$company_id} != {$tableName}.company_id")
->get(["{$table}.id"]);
if ($records->count()) {
@ -967,11 +872,6 @@ class CheckData extends Command
}
}
// foreach(User::cursor() as $user) {
// $records = Company::where('account_id',)
// }
}
public function pluralizeEntityType($type)

View File

@ -61,6 +61,8 @@ class CreateTestData extends Command
protected $invoice_repo;
protected $count;
/**
* Execute the console command.
*

View File

@ -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') {

View File

@ -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();
}

View File

@ -122,7 +122,7 @@ class TypeCheck extends Command
$client->save();
});
Company::cursor()->each(function ($company) {
Company::query()->cursor()->each(function ($company) {
$this->logMessage("Checking company {$company->id}");
$company->saveSettings($company->settings, $company);
});

View File

@ -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();

View File

@ -0,0 +1,101 @@
<?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\DataMapper\Analytics;
use Turbo124\Beacon\ExampleMetric\GenericMixedMetric;
class RevenueTrack extends GenericMixedMetric
{
/**
* The type of Sample.
*
* Monotonically incrementing counter
*
* - counter
*
* @var string
*/
public $type = 'mixed_metric';
/**
* The name of the counter.
* @var string
*/
public $name = 'app.revenue';
/**
* The datetime of the counter measurement.
*
* date("Y-m-d H:i:s")
*
*/
public $datetime;
/**
* The Client email
*
* @var string
*/
public $string_metric5 = 'email';
/**
* The AccountKey email
*
* @var string
*/
public $string_metric6 = 'key';
/**
* Product Type
*
* @var string
*/
public $string_metric7 = 'product';
/**
* Gateway Reference
*
* @var string
*/
public $string_metric8 = 'gateway_reference';
public $string_metric9 = 'entity_reference';
public $string_metric10 = 'gateway_type';
/**
* The counter
* set to 1.
*
* @var int
*/
public $int_metric1 = 1;
/**
* Amount Received
*
* @var double
*/
public $double_metric2 = 0;
public function __construct($string_metric5, $string_metric6, $int_metric1, $double_metric2, $string_metric7, $string_metric8, $string_metric9, $string_metric10)
{
$this->int_metric1 = $int_metric1;
$this->double_metric2 = $double_metric2;
$this->string_metric5 = $string_metric5;
$this->string_metric6 = $string_metric6;
$this->string_metric7 = $string_metric7;
$this->string_metric8 = $string_metric8;
$this->string_metric9 = $string_metric9;
$this->string_metric10 = $string_metric10;
}
}

View File

@ -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',
@ -812,7 +815,7 @@ class CompanySettings extends BaseSettings
* need to provide a fallback catch on old settings objects which will
* set new properties to the object prior to being returned.
*
* @param $settings
* @param \stdClass $settings
*
* @return stdClass
*/
@ -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
*

View File

@ -61,8 +61,13 @@ class InvoiceItem
public $tax_id = '';
public $task_id = '';
public $expense_id = '';
public static $casts = [
'task_id' => 'string',
'expense_id' => 'string',
'tax_id' => 'string',
'type_id' => 'string',
'quantity' => 'float',

View File

@ -250,8 +250,8 @@ class Rule extends BaseRule implements RuleInterface
// $this->reduced_tax_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->reduced_tax_rate;
// }
$this->tax_rate = $this->client->company->tax_data->regions->AU->subregions->{$this->client->company->country()->iso_3166_2}->tax_rate;
$this->reduced_tax_rate = $this->client->company->tax_data->regions->AU->subregions->{$this->client->company->country()->iso_3166_2}->tax_rate;
$this->tax_rate = $this->client->company->tax_data->regions->AU->subregions->{$this->client->company->country()->iso_3166_2}->tax_rate ?? 0;
$this->reduced_tax_rate = $this->client->company->tax_data->regions->AU->subregions->{$this->client->company->country()->iso_3166_2}->tax_rate ?? 0;
return $this;

View File

@ -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

View File

@ -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;

View File

@ -0,0 +1,36 @@
<?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\Events\Account;
use App\Models\Company;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
/**
* Class StripeConnectFailure.
*/
class StripeConnectFailure
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(public Company $company, public string $db)
{
}
public function broadcastOn()
{
return [];
}
}

View File

@ -0,0 +1,45 @@
<?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\Events\Vendor;
use App\Models\Company;
use App\Models\VendorContact;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
/**
* Class VendorContactLoggedIn.
*/
class VendorContactLoggedIn
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
*/
public function __construct(public VendorContact $contact, public Company $company, public array $event_vars)
{
}
/**
* Get the channels the event should broadcast on.
*
* @return Channel|array
*/
public function broadcastOn()
{
return [];
}
}

View File

@ -20,6 +20,7 @@ use App\Libraries\MultiDB;
use App\Models\DateFormat;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\App;
use Illuminate\Database\Eloquent\Builder;
use App\Transformers\ActivityTransformer;
class ActivityExport extends BaseExport
@ -39,10 +40,6 @@ class ActivityExport extends BaseExport
'address' => 'address',
];
private array $decorate_keys = [
];
public function __construct(Company $company, array $input)
{
$this->company = $company;
@ -50,47 +47,33 @@ class ActivityExport extends BaseExport
$this->entity_transformer = new ActivityTransformer();
}
public function run()
public function returnJson()
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
$query = $this->init();
$this->date_format = DateFormat::find($this->company->settings->date_format_id)->format;
$headerdisplay = $this->buildHeader();
//load the CSV document from a string
$this->csv = Writer::createFromString();
$header = collect($this->input['report_keys'])->map(function ($key, $value) use($headerdisplay){
return ['identifier' => $value, 'display_value' => $headerdisplay[$value]];
})->toArray();
ksort($this->entity_keys);
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
$report = $query->cursor()
->map(function ($resource) {
$row = $this->buildActivityRow($resource);
return $this->processMetaData($row, $resource);
})->toArray();
return array_merge(['columns' => $header], $report);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Activity::query()
->where('company_id', $this->company->id);
$query = $this->addDateRange($query);
$query->cursor()
->each(function ($entity) {
$this->buildRow($entity);
});
return $this->csv->toString();
}
private function buildRow(Activity $activity)
private function buildActivityRow(Activity $activity): array
{
$this->csv->insertOne([
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 : '',
@ -108,7 +91,57 @@ class ActivityExport extends BaseExport
'recurring_expense' => $activity->recurring_expense ? $activity->recurring_expense->number : '',
]),
$activity->ip,
]);
];
}
private function init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
$this->date_format = DateFormat::find($this->company->settings->date_format_id)->format;
// ksort($this->entity_keys);
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
}
$query = Activity::query()
->where('company_id', $this->company->id);
$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);
});
return $this->csv->toString();
}
private function buildRow(Activity $activity)
{
$this->csv->insertOne($this->buildActivityRow($activity));
}
@ -117,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;
}
}

View File

@ -11,19 +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
{
@ -43,8 +54,6 @@ class BaseExport
public string $client_description = 'All Clients';
public array $forced_keys = [];
protected array $vendor_report_keys = [
'address1' => 'vendor.address1',
'address2' => 'vendor.address2',
@ -57,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',
@ -94,7 +103,6 @@ class BaseExport
"state" => "client.state",
"postal_code" => "client.postal_code",
"country" => "client.country_id",
"custom_value4" => "contact.custom_value4",
"shipping_address1" => "client.shipping_address1",
"shipping_address2" => "client.shipping_address2",
"shipping_city" => "client.shipping_city",
@ -109,6 +117,15 @@ class BaseExport
"first_name" => "contact.first_name",
"last_name" => "contact.last_name",
"email" => "contact.email",
'custom_value1' => 'client.custom_value1',
'custom_value2' => 'client.custom_value2',
'custom_value3' => 'client.custom_value3',
'custom_value4' => 'client.custom_value4',
"contact_custom_value1" => "contact.custom_value1",
"contact_custom_value2" => "contact.custom_value2",
"contact_custom_value3" => "contact.custom_value3",
"contact_custom_value4" => "contact.custom_value4",
];
protected array $invoice_report_keys = [
@ -126,6 +143,7 @@ class BaseExport
"private_notes" => "invoice.private_notes",
"uses_inclusive_taxes" => "invoice.uses_inclusive_taxes",
"is_amount_discount" => "invoice.is_amount_discount",
"discount" => "invoice.discount",
"partial" => "invoice.partial",
"partial_due_date" => "invoice.partial_due_date",
"surcharge1" => "invoice.custom_surcharge1",
@ -136,6 +154,16 @@ class BaseExport
"tax_amount" => "invoice.total_taxes",
"assigned_user" => "invoice.assigned_user_id",
"user" => "invoice.user_id",
"custom_value1" => "invoice.custom_value1",
"custom_value2" => "invoice.custom_value2",
"custom_value3" => "invoice.custom_value3",
"custom_value4" => "invoice.custom_value4",
'tax_name1' => 'invoice.tax_name1',
'tax_name2' => 'invoice.tax_name2',
'tax_name3' => 'invoice.tax_name3',
'tax_rate1' => 'invoice.tax_rate1',
'tax_rate2' => 'invoice.tax_rate2',
'tax_rate3' => 'invoice.tax_rate3',
];
protected array $recurring_invoice_report_keys = [
@ -153,6 +181,7 @@ class BaseExport
"private_notes" => "recurring_invoice.private_notes",
"uses_inclusive_taxes" => "recurring_invoice.uses_inclusive_taxes",
"is_amount_discount" => "recurring_invoice.is_amount_discount",
"discount" => "recurring_invoice.discount",
"partial" => "recurring_invoice.partial",
"partial_due_date" => "recurring_invoice.partial_due_date",
"surcharge1" => "recurring_invoice.custom_surcharge1",
@ -164,17 +193,23 @@ class BaseExport
"assigned_user" => "recurring_invoice.assigned_user_id",
"user" => "recurring_invoice.user_id",
"frequency_id" => "recurring_invoice.frequency_id",
"next_send_date" => "recurring_invoice.next_send_date"
"next_send_date" => "recurring_invoice.next_send_date",
"custom_value1" => "recurring_invoice.custom_value1",
"custom_value2" => "recurring_invoice.custom_value2",
"custom_value3" => "recurring_invoice.custom_value3",
"custom_value4" => "recurring_invoice.custom_value4",
'tax_name1' => 'recurring_invoice.tax_name1',
'tax_name2' => 'recurring_invoice.tax_name2',
'tax_name3' => 'recurring_invoice.tax_name3',
'tax_rate1' => 'recurring_invoice.tax_rate1',
'tax_rate2' => 'recurring_invoice.tax_rate2',
'tax_rate3' => 'recurring_invoice.tax_rate3',
];
protected array $purchase_order_report_keys = [
'amount' => 'purchase_order.amount',
'balance' => 'purchase_order.balance',
'vendor' => 'purchase_order.vendor_id',
// 'custom_surcharge1' => 'purchase_order.custom_surcharge1',
// 'custom_surcharge2' => 'purchase_order.custom_surcharge2',
// 'custom_surcharge3' => 'purchase_order.custom_surcharge3',
// 'custom_surcharge4' => 'purchase_order.custom_surcharge4',
'custom_value1' => 'purchase_order.custom_value1',
'custom_value2' => 'purchase_order.custom_value2',
'custom_value3' => 'purchase_order.custom_value3',
@ -203,17 +238,41 @@ class BaseExport
'currency_id' => 'purchase_order.currency_id',
];
protected array $product_report_keys = [
// 'project' => 'project_id',
// 'vendor' => 'vendor_id',
'custom_value1' => 'custom_value1',
'custom_value2' => 'custom_value2',
'custom_value3' => 'custom_value3',
'custom_value4' => 'custom_value4',
'product_key' => 'product_key',
'notes' => 'notes',
'cost' => 'cost',
'price' => 'price',
'quantity' => 'quantity',
'tax_rate1' => 'tax_rate1',
'tax_rate2' => 'tax_rate2',
'tax_rate3' => 'tax_rate3',
'tax_name1' => 'tax_name1',
'tax_name2' => 'tax_name2',
'tax_name3' => 'tax_name3',
'image' => 'product_image',
'tax_category' => 'tax_id',
'max_quantity' => 'max_quantity',
'in_stock_quantity' => 'in_stock_quantity',
];
protected array $item_report_keys = [
"quantity" => "item.quantity",
"cost" => "item.cost",
"product_key" => "item.product_key",
"notes" => "item.notes",
"item_tax1" => "item.tax_name1",
"item_tax_rate1" => "item.tax_rate1",
"item_tax2" => "item.tax_name2",
"item_tax_rate2" => "item.tax_rate2",
"item_tax3" => "item.tax_name3",
"item_tax_rate3" => "item.tax_rate3",
"tax_name1" => "item.tax_name1",
"tax_rate1" => "item.tax_rate1",
"tax_name2" => "item.tax_name2",
"tax_rate2" => "item.tax_rate2",
"tax_name3" => "item.tax_name3",
"tax_rate3" => "item.tax_rate3",
"custom_value1" => "item.custom_value1",
"custom_value2" => "item.custom_value2",
"custom_value3" => "item.custom_value3",
@ -221,6 +280,9 @@ class BaseExport
"discount" => "item.discount",
"type" => "item.type_id",
"tax_category" => "item.tax_id",
'is_amount_discount' => 'item.is_amount_discount',
'line_total' => 'item.line_total',
'gross_line_total' => 'item.gross_line_total',
];
protected array $quote_report_keys = [
@ -242,6 +304,7 @@ class BaseExport
"private_notes" => "quote.private_notes",
"uses_inclusive_taxes" => "quote.uses_inclusive_taxes",
"is_amount_discount" => "quote.is_amount_discount",
"discount" => "quote.discount",
"partial" => "quote.partial",
"partial_due_date" => "quote.partial_due_date",
"surcharge1" => "quote.custom_surcharge1",
@ -252,6 +315,12 @@ class BaseExport
"tax_amount" => "quote.total_taxes",
"assigned_user" => "quote.assigned_user_id",
"user" => "quote.user_id",
'tax_name1' => 'quote.tax_name1',
'tax_name2' => 'quote.tax_name2',
'tax_name3' => 'quote.tax_name3',
'tax_rate1' => 'quote.tax_rate1',
'tax_rate2' => 'quote.tax_rate2',
'tax_rate3' => 'quote.tax_rate3',
];
protected array $credit_report_keys = [
@ -263,6 +332,7 @@ class BaseExport
"date" => "credit.date",
"due_date" => "credit.due_date",
"terms" => "credit.terms",
"discount" => "credit.discount",
"footer" => "credit.footer",
"status" => "credit.status",
"public_notes" => "credit.public_notes",
@ -275,6 +345,10 @@ class BaseExport
"surcharge2" => "credit.custom_surcharge2",
"surcharge3" => "credit.custom_surcharge3",
"surcharge4" => "credit.custom_surcharge4",
"custom_value1" => "credit.custom_value1",
"custom_value2" => "credit.custom_value2",
"custom_value3" => "credit.custom_value3",
"custom_value4" => "credit.custom_value4",
"exchange_rate" => "credit.exchange_rate",
"tax_amount" => "credit.total_taxes",
"assigned_user" => "credit.assigned_user_id",
@ -304,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',
@ -518,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];
@ -584,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();
@ -606,7 +682,7 @@ class BaseExport
}
if($transformer instanceof TaskTransformer) {
if($transformer instanceof TaskTransformer && ($entity->invoice ?? false)) {
$transformed_invoice = $transformer->includeInvoice($entity);
if(!$transformed_invoice)
@ -822,16 +898,31 @@ class BaseExport
}
}
/**
* Returns the merged array of
* the entity with the matching
* item report keys
*
* @param string $entity_report_keys
* @return array
*/
public function mergeItemsKeys(string $entity_report_keys): array
{
return array_merge($this->{$entity_report_keys}, $this->item_report_keys);
}
public function buildHeader() :array
{
$helper = new Helpers();
$header = [];
// nlog($this->input['report_keys']);
foreach (array_merge($this->input['report_keys'], $this->forced_keys) as $value) {
// nlog("header");
foreach ($this->input['report_keys'] as $value) {
$key = array_search($value, $this->entity_keys);
$original_key = $key;
// nlog("{$key} => {$value}");
$prefix = '';
if(!$key) {
@ -854,7 +945,6 @@ class BaseExport
$key = array_search($value, $this->payment_report_keys);
}
if(!$key) {
$prefix = ctrans('texts.quote')." ";
$key = array_search($value, $this->quote_report_keys);
@ -873,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) {
@ -892,8 +985,15 @@ class BaseExport
if(!$key) {
$prefix = '';
$key = array_search($value, $this->product_report_keys);
}
if(!$key) {
$prefix = '';
}
// nlog("key => {$key}");
$key = str_replace('item.', '', $key);
$key = str_replace('recurring_invoice.', '', $key);
$key = str_replace('purchase_order.', '', $key);
@ -906,11 +1006,42 @@ class BaseExport
$key = str_replace('contact.', '', $key);
$key = str_replace('payment.', '', $key);
$key = str_replace('expense.', '', $key);
$key = str_replace('product.', '', $key);
$key = str_replace('task.', '', $key);
if(in_array($key, ['quote1','quote2','quote3','quote4','credit1','credit2','credit3','credit4','purchase_order1','purchase_order2','purchase_order3','purchase_order4']))
if(stripos($value, 'custom_value') !== false)
{
$number = substr($key, -1);
$header[] = ctrans('texts.item') . " ". ctrans("texts.custom_value{$number}");
$parts = explode(".", $value);
if(count($parts) == 2 && in_array($parts[0], ['credit','quote','invoice','purchase_order','recurring_invoice'])){
$entity = "invoice".substr($parts[1], -1);
$prefix = ctrans("texts.".$parts[0]);
$fallback = "custom_value".substr($parts[1], -1);
$custom_field_label = $helper->makeCustomField($this->company->custom_fields, $entity);
if(strlen($custom_field_label) > 1)
$header[] = $custom_field_label;
else {
$header[] = $prefix . " ". ctrans("texts.{$fallback}");
}
}
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','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;
}
else{
$header[] = "{$prefix}" . ctrans("texts.{$key}");
}
}
else
{
@ -922,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;
}
}

View File

@ -11,14 +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\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
{
@ -72,16 +74,6 @@ class ClientExport extends BaseExport
'status' => 'status'
];
private array $decorate_keys = [
'client.country_id',
'client.shipping_country_id',
'client.currency',
'client.industry',
];
public array $forced_keys = [
];
public function __construct(Company $company, array $input)
{
$this->company = $company;
@ -90,7 +82,28 @@ class ClientExport extends BaseExport
$this->contact_transformer = new ClientContactTransformer();
}
public function run()
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 ($client) {
$row = $this->buildRow($client);
return $this->processMetaData($row, $client);
})->toArray();
return array_merge(['columns' => $header], $report);
}
public function init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
@ -98,16 +111,10 @@ class ClientExport 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->client_report_keys);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Client::query()->with('contacts')
->withTrashed()
->where('company_id', $this->company->id)
@ -115,6 +122,20 @@ class ClientExport 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 ($client) {
$this->csv->insertOne($this->buildRow($client));
@ -152,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'] = $value;
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'])) {

View File

@ -18,6 +18,7 @@ use App\Models\Company;
use App\Transformers\ClientContactTransformer;
use App\Transformers\ClientTransformer;
use App\Utils\Ninja;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -32,54 +33,6 @@ class ContactExport extends BaseExport
public string $date_key = 'created_at';
public array $entity_keys = [
'address1' => 'client.address1',
'address2' => 'client.address2',
'balance' => 'client.balance',
'city' => 'client.city',
'country' => 'client.country_id',
'credit_balance' => 'client.credit_balance',
'custom_value1' => 'client.custom_value1',
'custom_value2' => 'client.custom_value2',
'custom_value3' => 'client.custom_value3',
'custom_value4' => 'client.custom_value4',
'id_number' => 'client.id_number',
'industry' => 'client.industry_id',
'last_login' => 'client.last_login',
'name' => 'client.name',
'number' => 'client.number',
'paid_to_date' => 'client.paid_to_date',
'client_phone' => 'client.phone',
'postal_code' => 'client.postal_code',
'private_notes' => 'client.private_notes',
'public_notes' => 'client.public_notes',
'shipping_address1' => 'client.shipping_address1',
'shipping_address2' => 'client.shipping_address2',
'shipping_city' => 'client.shipping_city',
'shipping_country' => 'client.shipping_country_id',
'shipping_postal_code' => 'client.shipping_postal_code',
'shipping_state' => 'client.shipping_state',
'state' => 'client.state',
'vat_number' => 'client.vat_number',
'website' => 'client.website',
'currency' => 'client.currency',
'first_name' => 'contact.first_name',
'last_name' => 'contact.last_name',
'contact_phone' => 'contact.phone',
'contact_custom_value1' => 'contact.custom_value1',
'contact_custom_value2' => 'contact.custom_value2',
'contact_custom_value3' => 'contact.custom_value3',
'contact_custom_value4' => 'contact.custom_value4',
'email' => 'contact.email',
];
private array $decorate_keys = [
'client.country_id',
'client.shipping_country_id',
'client.currency',
'client.industry',
];
public function __construct(Company $company, array $input)
{
$this->company = $company;
@ -88,29 +41,39 @@ class ContactExport extends BaseExport
$this->contact_transformer = new ClientContactTransformer();
}
public function run()
private function init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
//load the CSV document from a string
$this->csv = Writer::createFromString();
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
$this->input['report_keys'] = array_values($this->client_report_keys);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = ClientContact::query()
->where('company_id', $this->company->id);
$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 ($contact) {
$this->csv->insertOne($this->buildRow($contact));
});
@ -118,6 +81,27 @@ class ContactExport 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 ($contact) {
$row = $this->buildRow($contact);
return $this->processMetaData($row, $contact);
})->toArray();
return array_merge(['columns' => $header], $report);
}
private function buildRow(ClientContact $contact) :array
{
$transformed_contact = false;
@ -129,14 +113,13 @@ class ContactExport extends BaseExport
foreach (array_values($this->input['report_keys']) as $key) {
$parts = explode('.', $key);
$keyval = array_search($key, $this->entity_keys);
if ($parts[0] == 'client' && array_key_exists($parts[1], $transformed_client)) {
$entity[$keyval] = $transformed_client[$parts[1]];
$entity[$key] = $transformed_client[$parts[1]];
} elseif ($parts[0] == 'contact' && array_key_exists($parts[1], $transformed_contact)) {
$entity[$keyval] = $transformed_contact[$parts[1]];
$entity[$key] = $transformed_contact[$parts[1]];
} else {
$entity[$keyval] = '';
$entity[$key] = '';
}
}

View File

@ -11,13 +11,15 @@
namespace App\Export\CSV;
use App\Libraries\MultiDB;
use App\Models\Company;
use App\Models\Credit;
use App\Transformers\CreditTransformer;
use App\Utils\Ninja;
use Illuminate\Support\Facades\App;
use App\Utils\Number;
use App\Models\Credit;
use League\Csv\Writer;
use App\Models\Company;
use App\Libraries\MultiDB;
use Illuminate\Support\Facades\App;
use App\Transformers\CreditTransformer;
use Illuminate\Database\Eloquent\Builder;
class CreditExport extends BaseExport
{
@ -28,51 +30,6 @@ class CreditExport 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',
'country' => 'country_id',
'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',
'invoice' => 'invoice_id',
'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',
'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',
];
private array $decorate_keys = [
'country',
'client',
'invoice',
'currency',
];
public function __construct(Company $company, array $input)
{
$this->company = $company;
@ -80,31 +37,82 @@ class CreditExport extends BaseExport
$this->credit_transformer = new CreditTransformer();
}
public function run()
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 ($credit) {
$row = $this->buildRow($credit);
return $this->processMetaData($row, $credit);
})->toArray();
return array_merge(['columns' => $header], $report);
}
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] == '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], $resource->client);
else
$clean_row[$key]['display_value'] = $row[$column_key];
}
return $clean_row;
}
private function init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
//load the CSV document from a string
$this->csv = Writer::createFromString();
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
$this->input['report_keys'] = array_values($this->credit_report_keys);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Credit::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(): string
{
$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 ($credit) {
$this->csv->insertOne($this->buildRow($credit));
@ -120,22 +128,22 @@ class CreditExport 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("credit.", "", $key), $this->entity_keys) ?? $key;
if(!$keyval)
$keyval = $key;
$credit_key = str_replace("credit.", "", $key);
$searched_credit_key = array_search(str_replace("credit.", "", $key), $this->credit_report_keys) ?? $key;
if (array_key_exists($key, $transformed_credit)) {
$entity[$keyval] = $transformed_credit[$key];
} elseif (array_key_exists($keyval, $transformed_credit)) {
if (isset($transformed_credit[$credit_key])) {
$entity[$keyval] = $transformed_credit[$credit_key];
} elseif (isset($transformed_credit[$keyval])) {
$entity[$keyval] = $transformed_credit[$keyval];
} elseif(isset($transformed_credit[$searched_credit_key])){
$entity[$keyval] = $transformed_credit[$searched_credit_key];
}
else {
$entity[$keyval] = $this->resolveKey($keyval, $credit, $this->credit_transformer);
}
}
return $this->decorateAdvancedFields($credit, $entity);

View File

@ -16,6 +16,7 @@ use App\Models\Company;
use App\Models\Document;
use App\Transformers\DocumentTransformer;
use App\Utils\Ninja;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -30,16 +31,11 @@ class DocumentExport extends BaseExport
public array $entity_keys = [
'record_type' => 'record_type',
// 'record_name' => 'record_name',
'name' => 'name',
'type' => 'type',
'created_at' => 'created_at',
];
private array $decorate_keys = [
];
public function __construct(Company $company, array $input)
{
$this->company = $company;
@ -47,28 +43,56 @@ class DocumentExport extends BaseExport
$this->entity_transformer = new DocumentTransformer();
}
public function run()
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 ($document) {
$row = $this->buildRow($document);
return $this->processMetaData($row, $document);
})->toArray();
return array_merge(['columns' => $header], $report);
}
private function init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
//load the CSV document from a string
$this->csv = Writer::createFromString();
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Document::query()->where('company_id', $this->company->id);
$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->csv->insertOne($this->buildRow($entity));

View File

@ -16,6 +16,7 @@ use App\Models\Company;
use App\Models\Expense;
use App\Transformers\ExpenseTransformer;
use App\Utils\Ninja;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -28,51 +29,6 @@ class ExpenseExport extends BaseExport
public Writer $csv;
public array $entity_keys = [
'amount' => 'expense.amount',
'category' => 'expense.category',
'client' => 'expense.client_id',
'custom_value1' => 'expense.custom_value1',
'custom_value2' => 'expense.custom_value2',
'custom_value3' => 'expense.custom_value3',
'custom_value4' => 'expense.custom_value4',
'currency' => 'expense.currency_id',
'date' => 'expense.date',
'exchange_rate' => 'expense.exchange_rate',
'converted_amount' => 'expense.foreign_amount',
'invoice_currency_id' => 'expense.invoice_currency_id',
'payment_date' => 'expense.payment_date',
'number' => 'expense.number',
'payment_type_id' => 'expense.payment_type_id',
'private_notes' => 'expense.private_notes',
'project' => 'expense.project_id',
'public_notes' => 'expense.public_notes',
'tax_amount1' => 'expense.tax_amount1',
'tax_amount2' => 'expense.tax_amount2',
'tax_amount3' => 'expense.tax_amount3',
'tax_name1' => 'expense.tax_name1',
'tax_name2' => 'expense.tax_name2',
'tax_name3' => 'expense.tax_name3',
'tax_rate1' => 'expense.tax_rate1',
'tax_rate2' => 'expense.tax_rate2',
'tax_rate3' => 'expense.tax_rate3',
'transaction_reference' => 'expense.transaction_reference',
'vendor' => 'expense.vendor_id',
'invoice' => 'expense.invoice_id',
'user' => 'expense.user',
'assigned_user' => 'expense.assigned_user',
];
private array $decorate_keys = [
'client',
'currency',
'invoice',
'category',
'vendor',
'project',
'payment_type_id',
];
public function __construct(Company $company, array $input)
{
$this->company = $company;
@ -80,24 +36,39 @@ class ExpenseExport extends BaseExport
$this->expense_transformer = new ExpenseTransformer();
}
public function run()
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 init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
//load the CSV document from a string
$this->csv = Writer::createFromString();
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
$this->input['report_keys'] = array_values($this->expense_report_keys);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Expense::query()
->with('client')
->withTrashed()
@ -106,6 +77,20 @@ class ExpenseExport 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 ($expense) {
$this->csv->insertOne($this->buildRow($expense));
@ -122,7 +107,6 @@ class ExpenseExport 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] == 'expense' && array_key_exists($parts[1], $transformed_expense)) {
$entity[$key] = $transformed_expense[$parts[1]];
@ -175,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;
}
}

View File

@ -20,6 +20,7 @@ use App\Libraries\MultiDB;
use App\Export\CSV\BaseExport;
use Illuminate\Support\Facades\App;
use App\Transformers\InvoiceTransformer;
use Illuminate\Database\Eloquent\Builder;
class InvoiceExport extends BaseExport
{
@ -29,56 +30,6 @@ class InvoiceExport 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',
'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_id' => 'currency_id',
'payment_number' => 'payment_number',
'payment_date' => 'payment_date',
'payment_amount' => 'payment_amount',
'method' => 'method',
];
private array $decorate_keys = [
'country',
'client',
'currency_id',
'status',
'vendor',
'project',
];
public function __construct(Company $company, array $input)
{
$this->company = $company;
@ -86,24 +37,19 @@ class InvoiceExport extends BaseExport
$this->invoice_transformer = new InvoiceTransformer();
}
public function run()
public function init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
//load the CSV document from a string
$this->csv = Writer::createFromString();
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
$this->input['report_keys'] = array_values($this->invoice_report_keys);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Invoice::query()
->withTrashed()
->with('client')
@ -112,10 +58,43 @@ class InvoiceExport extends BaseExport
$query = $this->addDateRange($query);
if(isset($this->input['status'])){
if(isset($this->input['status'])) {
$query = $this->addInvoiceStatusFilter($query, $this->input['status']);
}
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();
//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));
@ -131,24 +110,15 @@ class InvoiceExport 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("invoice.", "", $key), $this->entity_keys) ?? $key;
$parts = explode('.', $key);
if (is_array($parts) && $parts[0] == 'invoice' && array_key_exists($parts[1], $transformed_invoice)) {
$entity[$key] = $transformed_invoice[$parts[1]];
} else {
$entity[$key] = $this->resolveKey($key, $invoice, $this->invoice_transformer);
}
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];
}
else {
$entity[$keyval] = $this->resolveKey($keyval, $invoice, $this->invoice_transformer);
}
}
return $this->decorateAdvancedFields($invoice, $entity);
@ -156,32 +126,23 @@ class InvoiceExport extends BaseExport
private function decorateAdvancedFields(Invoice $invoice, array $entity) :array
{
if (in_array('country_id', $this->input['report_keys'])) {
$entity['country'] = $invoice->client->country ? ctrans("texts.country_{$invoice->client->country->name}") : '';
if (in_array('invoice.country_id', $this->input['report_keys'])) {
$entity['invoice.country_id'] = $invoice->client->country ? ctrans("texts.country_{$invoice->client->country->name}") : '';
}
if (in_array('currency_id', $this->input['report_keys'])) {
$entity['currency_id'] = $invoice->client->currency() ? $invoice->client->currency()->code : $invoice->company->currency()->code;
if (in_array('invoice.currency_id', $this->input['report_keys'])) {
$entity['invoice.currency_id'] = $invoice->client->currency() ? $invoice->client->currency()->code : $invoice->company->currency()->code;
}
if (in_array('client_id', $this->input['report_keys'])) {
$entity['client'] = $invoice->client->present()->name();
if (in_array('invoice.client_id', $this->input['report_keys'])) {
$entity['invoice.client_id'] = $invoice->client->present()->name();
}
if (in_array('status_id', $this->input['report_keys'])) {
$entity['status'] = $invoice->stringStatus($invoice->status_id);
if (in_array('invoice.status', $this->input['report_keys'])) {
$entity['invoice.status'] = $invoice->stringStatus($invoice->status_id);
}
// $payment_exists = $invoice->payments()->exists();
// $entity['payment_number'] = $payment_exists ? $invoice->payments()->pluck('number')->implode(',') : '';
// $entity['payment_date'] = $payment_exists ? $invoice->payments()->pluck('date')->implode(',') : '';
// $entity['payment_amount'] = $payment_exists ? Number::formatMoney($invoice->payments()->sum('paymentables.amount'), $invoice->company) : ctrans('texts.unpaid');
// $entity['method'] = $payment_exists ? $invoice->payments()->first()->translatedType() : "";
return $entity;
}
}

View File

@ -12,11 +12,11 @@
namespace App\Export\CSV;
use App\Libraries\MultiDB;
use App\Models\Client;
use App\Models\Company;
use App\Models\Invoice;
use App\Transformers\InvoiceTransformer;
use App\Utils\Ninja;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -31,64 +31,9 @@ class InvoiceItemExport extends BaseExport
private bool $force_keys = false;
public array $entity_keys = [
'amount' => 'amount',
'balance' => 'balance',
'client' => 'client_id',
'client_number' => 'client.number',
'client_id_number' => 'client.id_number',
'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',
'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',
'quantity' => 'item.quantity',
'cost' => 'item.cost',
'product_key' => 'item.product_key',
'buy_price' => 'item.product_cost',
'notes' => 'item.notes',
'discount' => 'item.discount',
'is_amount_discount' => 'item.is_amount_discount',
'tax_rate1' => 'item.tax_rate1',
'tax_rate2' => 'item.tax_rate2',
'tax_rate3' => 'item.tax_rate3',
'tax_name1' => 'item.tax_name1',
'tax_name2' => 'item.tax_name2',
'tax_name3' => 'item.tax_name3',
'line_total' => 'item.line_total',
'gross_line_total' => 'item.gross_line_total',
'invoice1' => 'item.custom_value1',
'invoice2' => 'item.custom_value2',
'invoice3' => 'item.custom_value3',
'invoice4' => 'item.custom_value4',
'tax_category' => 'item.tax_id',
'type' => 'item.type_id',
];
private array $storage_array = [];
private array $storage_item_array = [];
private array $decorate_keys = [
'client',
@ -103,37 +48,77 @@ class InvoiceItemExport extends BaseExport
$this->invoice_transformer = new InvoiceTransformer();
}
public function run()
public function init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
//load the CSV document from a string
$this->csv = Writer::createFromString();
if (count($this->input['report_keys']) == 0) {
$this->force_keys = true;
$this->input['report_keys'] = array_values($this->entity_keys);
$this->input['report_keys'] = array_values($this->mergeItemsKeys('invoice_report_keys'));
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$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);
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();
$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);
}
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->iterateItems($invoice);
});
$this->csv->insertAll($this->storage_array);
return $this->csv->toString();
}
@ -146,46 +131,32 @@ class InvoiceItemExport extends BaseExport
foreach ($invoice->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.")) {
$key = str_replace("item.", "", $key);
$keyval = $key;
$keyval = str_replace("custom_value", "invoice", $key);
if($key == 'type_id')
$keyval = 'type';
$key = 'type';
if($key == 'tax_id')
$keyval = 'tax_category';
$key = 'tax_category';
if (property_exists($item, $key)) {
$item_array[$keyval] = $item->{$key};
} else {
$item_array[$keyval] = '';
$item_array[$key] = $item->{$key};
}
else {
$item_array[$key] = '';
}
}
$entity = [];
foreach (array_values($this->input['report_keys']) as $key) { //create an array of report keys only
$keyval = array_search($key, $this->entity_keys);
if (array_key_exists($key, $transformed_items)) {
$entity[$keyval] = $transformed_items[$key];
} else {
$entity[$keyval] = "";
}
}
$transformed_items = array_merge($transformed_invoice, $item_array);
$entity = $this->decorateAdvancedFields($invoice, $transformed_items);
$this->csv->insertOne($entity);
$this->storage_array[] = $entity;
}
}
@ -196,23 +167,19 @@ class InvoiceItemExport 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("invoice.", "", $key), $this->entity_keys) ?? $key;
}
$parts = explode('.', $key);
if(!$keyval) {
$keyval = $key;
}
if(is_array($parts) && $parts[0] == 'item')
continue;
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] == 'invoice' && array_key_exists($parts[1], $transformed_invoice)) {
$entity[$key] = $transformed_invoice[$parts[1]];
}else if (array_key_exists($key, $transformed_invoice)) {
$entity[$key] = $transformed_invoice[$key];
}
else {
$entity[$keyval] = $this->resolveKey($keyval, $invoice, $this->invoice_transformer);
$entity[$key] = $this->resolveKey($key, $invoice, $this->invoice_transformer);
}
}
@ -233,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;
}
}

View File

@ -16,6 +16,7 @@ use App\Models\Company;
use App\Models\Payment;
use App\Transformers\PaymentTransformer;
use App\Utils\Ninja;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -27,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;
@ -68,24 +35,19 @@ class PaymentExport extends BaseExport
$this->entity_transformer = new PaymentTransformer();
}
public function run()
private function init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
//load the CSV document from a string
$this->csv = Writer::createFromString();
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
$this->input['report_keys'] = array_values($this->payment_report_keys);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Payment::query()
->withTrashed()
->where('company_id', $this->company->id)
@ -93,6 +55,39 @@ class PaymentExport extends BaseExport
$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();
//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->csv->insertOne($this->buildRow($entity));
@ -108,24 +103,17 @@ class PaymentExport 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("payment.", "", $key), $this->entity_keys) ?? $key;
$parts = explode('.', $key);
if (is_array($parts) && $parts[0] == 'payment' && 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, $payment, $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, $payment, $this->entity_transformer);
}
}
return $this->decorateAdvancedFields($payment, $entity);

View File

@ -13,10 +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\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -28,31 +28,6 @@ class ProductExport extends BaseExport
public Writer $csv;
public array $entity_keys = [
'project' => 'project_id',
'vendor' => 'vendor_id',
'custom_value1' => 'custom_value1',
'custom_value2' => 'custom_value2',
'custom_value3' => 'custom_value3',
'custom_value4' => 'custom_value4',
'product_key' => 'product_key',
'notes' => 'notes',
'cost' => 'cost',
'price' => 'price',
'quantity' => 'quantity',
'tax_rate1' => 'tax_rate1',
'tax_rate2' => 'tax_rate2',
'tax_rate3' => 'tax_rate3',
'tax_name1' => 'tax_name1',
'tax_name2' => 'tax_name2',
'tax_name3' => 'tax_name3',
];
private array $decorate_keys = [
'vendor',
'project',
];
public function __construct(Company $company, array $input)
{
$this->company = $company;
@ -60,24 +35,38 @@ class ProductExport extends BaseExport
$this->entity_transformer = new ProductTransformer();
}
public function run()
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 init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
//load the CSV document from a string
$this->csv = Writer::createFromString();
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
$this->input['report_keys'] = array_values($this->product_report_keys);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Product::query()
->withTrashed()
->where('company_id', $this->company->id)
@ -85,6 +74,21 @@ class ProductExport 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->csv->insertOne($this->buildRow($entity));
@ -100,7 +104,7 @@ class ProductExport extends BaseExport
$entity = [];
foreach (array_values($this->input['report_keys']) as $key) {
$keyval = array_search($key, $this->entity_keys);
$keyval = array_search($key, $this->product_report_keys);
if (array_key_exists($key, $transformed_entity)) {
$entity[$keyval] = $transformed_entity[$key];

View File

@ -85,7 +85,7 @@ class ProductSalesExport extends BaseExport
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
$this->products = Product::where('company_id', $this->company->id)->withTrashed()->get();
$this->products = Product::query()->where('company_id', $this->company->id)->withTrashed()->get();
//load the CSV document from a string
$this->csv = Writer::createFromString();
@ -187,6 +187,7 @@ class ProductSalesExport extends BaseExport
$product = $this->getProduct($entity['product_key']);
$entity['cost'] = $product->cost ?? 0;
/** @var float $unit_cost */
$unit_cost = $entity['cost'] == 0 ? 1 : $entity['cost'];
$entity['client'] = $invoice->client->present()->name();

View File

@ -11,14 +11,15 @@
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 App\Utils\Number;
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 App\Transformers\PurchaseOrderTransformer;
use Illuminate\Database\Eloquent\Builder;
class PurchaseOrderExport extends BaseExport
{
@ -81,24 +82,19 @@ class PurchaseOrderExport extends BaseExport
$this->purchase_order_transformer = new PurchaseOrderTransformer();
}
public function run()
public function init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
//load the CSV document from a string
$this->csv = Writer::createFromString();
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
$this->input['report_keys'] = array_values($this->purchase_order_report_keys);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = PurchaseOrder::query()
->withTrashed()
->with('vendor')
@ -107,9 +103,39 @@ class PurchaseOrderExport extends BaseExport
$query = $this->addDateRange($query);
// if(isset($this->input['status'])) {
// $query = $this->addPurchaseOrderStatusFilter($query, $this->input['status']);
// }
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();
//load the CSV document from a string
$this->csv = Writer::createFromString();
//insert the header
$this->csv->insertOne($this->buildHeader());
$query->cursor()
->each(function ($purchase_order) {
@ -126,23 +152,16 @@ class PurchaseOrderExport 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("purchase_order.", "", $key), $this->entity_keys) ?? $key;
}
$parts = explode('.', $key);
if(!$keyval) {
$keyval = $key;
}
if (array_key_exists($key, $transformed_purchase_order)) {
$entity[$keyval] = $transformed_purchase_order[$key];
} elseif (array_key_exists($keyval, $transformed_purchase_order)) {
$entity[$keyval] = $transformed_purchase_order[$keyval];
if (is_array($parts) && $parts[0] == 'purchase_order' && array_key_exists($parts[1], $transformed_purchase_order)) {
$entity[$key] = $transformed_purchase_order[$parts[1]];
} else {
$entity[$keyval] = $this->resolveKey($keyval, $purchase_order, $this->purchase_order_transformer);
$entity[$key] = $this->resolveKey($key, $purchase_order, $this->purchase_order_transformer);
}
}
return $this->decorateAdvancedFields($purchase_order, $entity);

View File

@ -11,13 +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\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
{
@ -30,70 +31,9 @@ class PurchaseOrderItemExport extends BaseExport
private bool $force_keys = false;
public array $entity_keys = [
'amount' => 'amount',
'balance' => 'balance',
'vendor' => 'vendor_id',
'vendor_number' => 'vendor.number',
'vendor_id_number' => 'vendor.id_number',
// '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',
'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',
'quantity' => 'item.quantity',
'cost' => 'item.cost',
'product_key' => 'item.product_key',
'buy_price' => 'item.product_cost',
'notes' => 'item.notes',
'discount' => 'item.discount',
'is_amount_discount' => 'item.is_amount_discount',
'tax_rate1' => 'item.tax_rate1',
'tax_rate2' => 'item.tax_rate2',
'tax_rate3' => 'item.tax_rate3',
'tax_name1' => 'item.tax_name1',
'tax_name2' => 'item.tax_name2',
'tax_name3' => 'item.tax_name3',
'line_total' => 'item.line_total',
'gross_line_total' => 'item.gross_line_total',
'purchase_order1' => 'item.custom_value1',
'purchase_order2' => 'item.custom_value2',
'purchase_order3' => 'item.custom_value3',
'purchase_order4' => 'item.custom_value4',
'tax_category' => 'item.tax_id',
'type' => 'item.type_id',
];
private array $storage_array = [];
private array $decorate_keys = [
'client',
'currency_id',
'status'
];
private array $storage_item_array = [];
public function __construct(Company $company, array $input)
{
@ -102,25 +42,19 @@ class PurchaseOrderItemExport extends BaseExport
$this->purchase_order_transformer = new PurchaseOrderTransformer();
}
public function run()
private function init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
//load the CSV document from a string
$this->csv = Writer::createFromString();
if (count($this->input['report_keys']) == 0) {
$this->force_keys = true;
$this->input['report_keys'] = array_values($this->entity_keys);
$this->input['report_keys'] = array_values($this->mergeItemsKeys('purchase_order_report_keys'));
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = PurchaseOrder::query()
->withTrashed()
->with('vendor')->where('company_id', $this->company->id)
@ -128,12 +62,54 @@ class PurchaseOrderItemExport extends BaseExport
$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();
$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);
}
public function run()
{
//load the CSV document from a string
$this->csv = Writer::createFromString();
$query = $this->init();
//insert the header
$this->csv->insertOne($this->buildHeader());
$query->cursor()
->each(function ($purchase_order) {
$this->iterateItems($purchase_order);
});
$this->csv->insertAll($this->storage_array);
return $this->csv->toString();
}
private function iterateItems(PurchaseOrder $purchase_order)
@ -145,16 +121,12 @@ 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.")) {
$key = str_replace("item.", "", $key);
$keyval = $key;
$keyval = str_replace("custom_value", "purchase_order", $key);
if($key == 'type_id') {
$keyval = 'type';
}
@ -164,29 +136,17 @@ class PurchaseOrderItemExport extends BaseExport
}
if (property_exists($item, $key)) {
$item_array[$keyval] = $item->{$key};
$item_array[$key] = $item->{$key};
} else {
$item_array[$keyval] = '';
$item_array[$key] = '';
}
}
}
$entity = [];
foreach (array_values($this->input['report_keys']) as $key) { //create an array of report keys only
$keyval = array_search($key, $this->entity_keys);
if (array_key_exists($key, $transformed_items)) {
$entity[$keyval] = $transformed_items[$key];
} else {
$entity[$keyval] = "";
}
}
$transformed_items = array_merge($transformed_purchase_order, $item_array);
$entity = $this->decorateAdvancedFields($purchase_order, $transformed_items);
$this->csv->insertOne($entity);
$this->storage_array[] = $entity;
}
}
@ -197,22 +157,18 @@ class PurchaseOrderItemExport extends BaseExport
$entity = [];
foreach (array_values($this->input['report_keys']) as $key) {
$keyval = array_search($key, $this->entity_keys);
$parts = explode('.', $key);
if(!$keyval) {
$keyval = array_search(str_replace("purchase_order.", "", $key), $this->entity_keys) ?? $key;
if(is_array($parts) && $parts[0] == 'item') {
continue;
}
if(!$keyval) {
$keyval = $key;
}
if (array_key_exists($key, $transformed_purchase_order)) {
$entity[$keyval] = $transformed_purchase_order[$key];
} elseif (array_key_exists($keyval, $transformed_purchase_order)) {
$entity[$keyval] = $transformed_purchase_order[$keyval];
if (is_array($parts) && $parts[0] == 'purchase_order' && array_key_exists($parts[1], $transformed_purchase_order)) {
$entity[$key] = $transformed_purchase_order[$parts[1]];
} elseif (array_key_exists($key, $transformed_purchase_order)) {
$entity[$key] = $transformed_purchase_order[$key];
} else {
$entity[$keyval] = $this->resolveKey($keyval, $purchase_order, $this->purchase_order_transformer);
$entity[$key] = $this->resolveKey($key, $purchase_order, $this->purchase_order_transformer);
}
}
@ -242,4 +198,5 @@ class PurchaseOrderItemExport extends BaseExport
return $entity;
}
}

View File

@ -16,6 +16,7 @@ use App\Models\Company;
use App\Models\Quote;
use App\Transformers\QuoteTransformer;
use App\Utils\Ninja;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -28,43 +29,6 @@ class QuoteExport 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',
'valid_until' => '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',
'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',
'invoice' => 'invoice_id',
];
private array $decorate_keys = [
'client',
'currency',
@ -78,24 +42,20 @@ class QuoteExport extends BaseExport
$this->quote_transformer = new QuoteTransformer();
}
public function run()
private function init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
//load the CSV document from a string
$this->csv = Writer::createFromString();
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
$this->input['report_keys'] = array_values($this->quote_report_keys);
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Quote::query()
->withTrashed()
->with('client')
@ -104,6 +64,41 @@ class QuoteExport extends BaseExport
$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()
{
//load the CSV document from a string
$this->csv = Writer::createFromString();
$query = $this->init();
//insert the header
$this->csv->insertOne($this->buildHeader());
$query->cursor()
->each(function ($quote) {
$this->csv->insertOne($this->buildRow($quote));
@ -114,50 +109,42 @@ class QuoteExport extends BaseExport
private function buildRow(Quote $quote) :array
{
$transformed_entity = $this->quote_transformer->transform($quote);
$transformed_invoice = $this->quote_transformer->transform($quote);
$entity = [];
foreach (array_values($this->input['report_keys']) as $key) {
$keyval = array_search($key, $this->entity_keys);
if(!$keyval) {
$keyval = array_search(str_replace("invoice.", "", $key), $this->entity_keys) ?? $key;
$parts = explode('.', $key);
if (is_array($parts) && $parts[0] == 'quote' && array_key_exists($parts[1], $transformed_invoice)) {
$entity[$key] = $transformed_invoice[$parts[1]];
} else {
$entity[$key] = $this->resolveKey($key, $quote, $this->quote_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, $quote, $this->quote_transformer);
}
}
return $this->decorateAdvancedFields($quote, $entity);
}
private function decorateAdvancedFields(Quote $quote, array $entity) :array
{
if (in_array('currency_id', $this->input['report_keys'])) {
$entity['currency'] = $quote->client->currency()->code;
if (in_array('quote.currency_id', $this->input['report_keys'])) {
$entity['quote.currency'] = $quote->client->currency()->code;
}
if (in_array('client_id', $this->input['report_keys'])) {
$entity['client'] = $quote->client->present()->name();
if (in_array('quote.client_id', $this->input['report_keys'])) {
$entity['quote.client'] = $quote->client->present()->name();
}
if (in_array('status_id', $this->input['report_keys'])) {
$entity['status'] = $quote->stringStatus($quote->status_id);
if (in_array('quote.status', $this->input['report_keys'])) {
$entity['quote.status'] = $quote->stringStatus($quote->status_id);
}
if (in_array('invoice_id', $this->input['report_keys'])) {
$entity['invoice'] = $quote->invoice ? $quote->invoice->number : '';
if (in_array('quote.invoice_id', $this->input['report_keys'])) {
$entity['quote.invoice'] = $quote->invoice ? $quote->invoice->number : '';
}
return $entity;

View File

@ -16,6 +16,7 @@ use App\Models\Company;
use App\Models\Quote;
use App\Transformers\QuoteTransformer;
use App\Utils\Ninja;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use League\Csv\Writer;
@ -28,63 +29,8 @@ class QuoteItemExport 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',
'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',
'quantity' => 'item.quantity',
'cost' => 'item.cost',
'product_key' => 'item.product_key',
'buy_price' => 'item.product_cost',
'cost' => 'item.cost',
'notes' => 'item.notes',
'discount' => 'item.discount',
'is_amount_discount' => 'item.is_amount_discount',
'tax_rate1' => 'item.tax_rate1',
'tax_rate2' => 'item.tax_rate2',
'tax_rate3' => 'item.tax_rate3',
'tax_name1' => 'item.tax_name1',
'tax_name2' => 'item.tax_name2',
'tax_name3' => 'item.tax_name3',
'line_total' => 'item.line_total',
'gross_line_total' => 'item.gross_line_total',
'quote1' => 'item.custom_value1',
'quote2' => 'item.custom_value2',
'quote3' => 'item.custom_value3',
'quote4' => 'item.custom_value4',
'tax_category' => 'item.tax_id',
'type' => 'item.type_id',
];
private array $storage_array = [];
private array $storage_item_array = [];
private array $decorate_keys = [
'client',
@ -98,24 +44,19 @@ class QuoteItemExport extends BaseExport
$this->quote_transformer = new QuoteTransformer();
}
public function run()
public function init(): Builder
{
MultiDB::setDb($this->company->db);
App::forgetInstance('translator');
App::setLocale($this->company->locale());
$t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings));
//load the CSV document from a string
$this->csv = Writer::createFromString();
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
$this->input['report_keys'] = array_values($this->mergeItemsKeys('quote_report_keys'));
}
//insert the header
$this->csv->insertOne($this->buildHeader());
$query = Quote::query()
->withTrashed()
->with('client')->where('company_id', $this->company->id)
@ -123,12 +64,58 @@ class QuoteItemExport extends BaseExport
$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();
$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);
}
public function run()
{
//load the CSV document from a string
$this->csv = Writer::createFromString();
$query = $this->init();
//insert the header
$this->csv->insertOne($this->buildHeader());
$query->cursor()
->each(function ($quote) {
$this->iterateItems($quote);
});
$this->csv->insertAll($this->storage_array);
return $this->csv->toString();
}
private function iterateItems(Quote $quote)
@ -137,51 +124,34 @@ class QuoteItemExport extends BaseExport
$transformed_items = [];
$transformed_items = [];
foreach ($quote->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.")) {
$key = str_replace("item.", "", $key);
$keyval = $key;
$keyval = str_replace("custom_value", "quote", $key);
if($key == 'type_id')
$keyval = 'type';
$key = 'type';
if($key == 'tax_id')
$keyval = 'tax_category';
$key = 'tax_category';
if (property_exists($item, $key)) {
$item_array[$keyval] = $item->{$key};
} else {
$item_array[$keyval] = '';
$item_array[$key] = $item->{$key};
}
else {
$item_array[$key] = '';
}
}
$entity = [];
foreach (array_values($this->input['report_keys']) as $key) { //create an array of report keys only
$keyval = array_search($key, $this->entity_keys);
if (array_key_exists($key, $transformed_items)) {
$entity[$keyval] = $transformed_items[$key];
} else {
$entity[$keyval] = "";
}
}
$transformed_items = array_merge($transformed_quote, $item_array);
$entity = $this->decorateAdvancedFields($quote, $transformed_items);
$this->csv->insertOne($entity);
$this->storage_array[] = $entity;
}
}
@ -192,25 +162,22 @@ class QuoteItemExport 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("quote.", "", $key), $this->entity_keys) ?? $key;
$parts = explode('.', $key);
if(is_array($parts) && $parts[0] == 'item') {
continue;
}
if(!$keyval) {
$keyval = $key;
if (is_array($parts) && $parts[0] == 'quote' && array_key_exists($parts[1], $transformed_quote)) {
$entity[$key] = $transformed_quote[$parts[1]];
} elseif (array_key_exists($key, $transformed_quote)) {
$entity[$key] = $transformed_quote[$key];
} else {
$entity[$key] = $this->resolveKey($key, $quote, $this->quote_transformer);
}
}
if (array_key_exists($key, $transformed_quote)) {
$entity[$keyval] = $transformed_quote[$key];
} elseif (array_key_exists($keyval, $transformed_quote)) {
$entity[$keyval] = $transformed_quote[$keyval];
}
else {
$entity[$keyval] = $this->resolveKey($keyval, $quote, $this->quote_transformer);
}
}
return $this->decorateAdvancedFields($quote, $entity);
}

View File

@ -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,56 +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',
'next_send_date' => 'next_send_date'
];
private array $decorate_keys = [
'country',
'client',
'currency',
'status',
'vendor',
'project',
];
public function __construct(Company $company, array $input)
{
$this->company = $company;
@ -85,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');
@ -93,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));
@ -118,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);
@ -125,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);
}
}
@ -175,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;

View File

@ -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,52 +67,87 @@ 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);
}
}
private function iterateLogs(Task $task, array $entity)
@ -163,39 +171,40 @@ 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;

View File

@ -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,46 +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 array $forced_keys = [
// 'vendor.status'
];
public function __construct(Company $company, array $input)
{
$this->company = $company;
@ -79,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());
@ -91,12 +53,9 @@ 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)
@ -104,6 +63,37 @@ class VendorExport extends BaseExport
$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));
@ -114,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);
@ -127,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);
}
}

View File

@ -32,6 +32,7 @@ class ClientFactory
$client->is_deleted = 0;
$client->client_hash = Str::random(40);
$client->settings = ClientSettings::defaults();
$client->classification = '';
return $client;
}

View File

@ -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;
}

View File

@ -65,6 +65,7 @@ class RecurringExpenseToExpenseFactory
$expense->tax_amount3 = $recurring_expense->tax_amount3 ?: 0;
$expense->uses_inclusive_taxes = $recurring_expense->uses_inclusive_taxes;
$expense->calculate_tax_by_amount = $recurring_expense->calculate_tax_by_amount;
$expense->invoice_currency_id = $recurring_expense->invoice_currency_id;
return $expense;
}

View File

@ -28,6 +28,7 @@ class VendorFactory
$vendor->country_id = 4;
$vendor->is_deleted = 0;
$vendor->vendor_hash = Str::random(40);
$vendor->classification = '';
return $vendor;
}

View File

@ -88,6 +88,11 @@ class CreditFilters extends QueryFilters
->orWhere('credits.custom_value4', 'like', '%'.$filter.'%')
->orWhereHas('client', function ($q) use ($filter) {
$q->where('name', 'like', '%'.$filter.'%');
})
->orWhereHas('client.contacts', function ($q) use ($filter) {
$q->where('first_name', 'like', '%'.$filter.'%')
->orWhere('last_name', 'like', '%'.$filter.'%')
->orWhere('email', 'like', '%'.$filter.'%');
});
});
}

View File

@ -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]);

View File

@ -116,6 +116,11 @@ class InvoiceFilters extends QueryFilters
->orWhere('custom_value4', 'like', '%'.$filter.'%')
->orWhereHas('client', function ($q) use ($filter) {
$q->where('name', 'like', '%'.$filter.'%');
})
->orWhereHas('client.contacts', function ($q) use ($filter) {
$q->where('first_name', 'like', '%'.$filter.'%')
->orWhere('last_name', 'like', '%'.$filter.'%')
->orWhere('email', 'like', '%'.$filter.'%');
});
});
}
@ -224,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.

View File

@ -12,7 +12,9 @@
namespace App\Filters;
use App\Models\Payment;
use Illuminate\Support\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Contracts\Database\Eloquent\Builder as EloquentBuilder;
/**
* PaymentFilters.
@ -43,6 +45,11 @@ class PaymentFilters extends QueryFilters
->orWhere('custom_value4', 'like', '%'.$filter.'%')
->orWhereHas('client', function ($q) use ($filter) {
$q->where('name', 'like', '%'.$filter.'%');
})
->orWhereHas('client.contacts', function ($q) use ($filter) {
$q->where('first_name', 'like', '%'.$filter.'%')
->orWhere('last_name', 'like', '%'.$filter.'%')
->orWhere('email', 'like', '%'.$filter.'%');
});
});
}
@ -117,6 +124,8 @@ class PaymentFilters extends QueryFilters
/**
* Returns a list of payments that can be matched to bank transactions
* @param ?string $value
* @return Builder
*/
public function match_transactions($value = 'true'): Builder
{
@ -124,7 +133,7 @@ class PaymentFilters extends QueryFilters
if ($value == 'true') {
return $this->builder
->where('is_deleted', 0)
->where(function ($query) {
->where(function (Builder $query) {
$query->whereNull('transaction_id')
->orWhere("transaction_id", "")
->company();
@ -169,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.
*

View File

@ -72,8 +72,8 @@ abstract class QueryFilters
/**
* Apply the filters to the builder.
*
* @param Builder $builder
* @return Builder
* @param \Illuminate\Database\Eloquent\Builder $builder
* @return \Illuminate\Database\Eloquent\Builder
*/
public function apply(Builder $builder)
{
@ -239,7 +239,11 @@ abstract class QueryFilters
}
}
/**
*
* @param string $value
* @return \Illuminate\Database\Eloquent\Builder
*/
public function is_deleted($value = 'true')
{
if ($value == 'true') {
@ -294,7 +298,7 @@ abstract class QueryFilters
{
return $this->builder->where(function ($query) {
$query->whereHas('client', function ($sub_query) {
$sub_query->where('is_deleted', 0);
$sub_query->where('is_deleted', 0)->where('deleted_at', null);
})->orWhere('client_id', null);
});
}
@ -306,7 +310,7 @@ abstract class QueryFilters
{
return $this->builder->where(function ($query) {
$query->whereHas('vendor', function ($sub_query) {
$sub_query->where('is_deleted', 0);
$sub_query->where('is_deleted', 0)->where('deleted_at', null);
})->orWhere('vendor_id', null);
});
}

View File

@ -40,6 +40,11 @@ class QuoteFilters extends QueryFilters
->orWhere('custom_value4', 'like', '%'.$filter.'%')
->orWhereHas('client', function ($q) use ($filter) {
$q->where('name', 'like', '%'.$filter.'%');
})
->orWhereHas('client.contacts', function ($q) use ($filter) {
$q->where('first_name', 'like', '%'.$filter.'%')
->orWhere('last_name', 'like', '%'.$filter.'%')
->orWhere('email', 'like', '%'.$filter.'%');
});
});
}

View File

@ -42,6 +42,11 @@ class RecurringInvoiceFilters extends QueryFilters
->orWhere('custom_value4', 'like', '%'.$filter.'%')
->orWhereHas('client', function ($q) use ($filter) {
$q->where('name', 'like', '%'.$filter.'%');
})
->orWhereHas('client.contacts', function ($q) use ($filter) {
$q->where('first_name', 'like', '%'.$filter.'%')
->orWhere('last_name', 'like', '%'.$filter.'%')
->orWhere('email', 'like', '%'.$filter.'%');
});
});
}

View File

@ -45,6 +45,11 @@ class TaskFilters extends QueryFilters
})
->orWhereHas('client', function ($q) use ($filter) {
$q->where('name', 'like', '%'.$filter.'%');
})
->orWhereHas('client.contacts', function ($q) use ($filter) {
$q->where('first_name', 'like', '%'.$filter.'%')
->orWhere('last_name', 'like', '%'.$filter.'%')
->orWhere('email', 'like', '%'.$filter.'%');
});
});
}

View File

@ -127,8 +127,7 @@ class UserFilters extends QueryFilters
$user_array = $this->transformKeys(explode(',', $user_id));
return $this->builder->where(function ($query) use ($user_array) {
$query->whereNotIn('id', $user_array)
->where('account_id', auth()->user()->account_id);
$query->whereNotIn('id', $user_array);
});
}
}

View File

@ -0,0 +1,111 @@
<?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\Helpers\Bank\Yodlee\DTO;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Attributes\MapInputName;
use Spatie\LaravelData\Attributes\MapOutputName;
use Illuminate\Support\Collection;
/**
* [
"account": [
[
"CONTAINER": "bank",
"providerAccountId": 1005,
"accountName": "Business Acct",
"accountStatus": "ACTIVE",
"accountNumber": "1011",
"aggregationSource": "USER",
"isAsset": true,
"balance": [
"currency": "AUD",
"amount": 304.98,
],
"id": 10139315,
"includeInNetWorth": true,
"providerId": "3857",
"providerName": "Bank",
"isManual": false,
"availableBalance": {#2966
"currency": "AUD",
"amount": 304.98,
],
"currentBalance": [
"currency": "AUD",
"amount": 3044.98,
],
"accountType": "CHECKING",
"displayedName": "after David",
"createdDate": "2023-01-10T08:29:07Z",
"classification": "SMALL_BUSINESS",
"lastUpdated": "2023-08-01T23:50:13Z",
"nickname": "Business ",
"bankTransferCode": [
[
"id": "062",
"type": "BSB",
],
],
"dataset": [
[
"name": "BASIC_AGG_DATA",
"additionalStatus": "AVAILABLE_DATA_RETRIEVED",
"updateEligibility": "ALLOW_UPDATE",
"lastUpdated": "2023-08-01T23:49:53Z",
"lastUpdateAttempt": "2023-08-01T23:49:53Z",
"nextUpdateScheduled": "2023-08-03T14:45:14Z",
],
],
],
],
];
*/
class AccountSummary extends Data
{
public ?int $id;
#[MapInputName('CONTAINER')]
public ?string $account_type = '';
#[MapInputName('accountName')]
public ?string $account_name = '';
#[MapInputName('accountStatus')]
public ?string $account_status = '';
#[MapInputName('accountNumber')]
public ?string $account_number = '';
#[MapInputName('providerAccountId')]
public int $provider_account_id;
#[MapInputName('providerId')]
public ?string $provider_id = '';
#[MapInputName('providerName')]
public ?string $provider_name = '';
public ?string $nickname = '';
public ?float $current_balance = 0;
public ?string $account_currency = '';
public static function prepareForPipeline(Collection $properties) : Collection
{
$properties->put('current_balance', $properties['currentBalance']['amount'] ?? 0);
$properties->put('account_currency', $properties['currentBalance']['currency'] ?? 0);
return $properties;
}
}

View File

@ -64,6 +64,7 @@ use App\Helpers\Bank\AccountTransformerInterface;
class AccountTransformer implements AccountTransformerInterface
{
public function transform($yodlee_account)
{
$data = [];
@ -93,13 +94,54 @@ class AccountTransformer implements AccountTransformerInterface
$account_currency = $account->balance->currency ?? '';
}
$account_status = $account->accountStatus;
if(property_exists($account, 'dataset')){
$dataset = $account->dataset[0];
$status = false;
$update = false;
match($dataset->additionalStatus ?? ''){
'LOGIN_IN_PROGRESS' => $status = 'Data retrieval in progress.',
'USER_INPUT_REQUIRED' => $status = 'Please reconnect your account, authentication required.',
'LOGIN_SUCCESS' => $status = 'Data retrieval in progress',
'ACCOUNT_SUMMARY_RETRIEVED' => $status = 'Account summary retrieval in progress.',
'NEVER_INITIATED' => $status = 'Upstream working on connecting to your account.',
'LOGIN_FAILED' => $status = 'Authentication failed, please try reauthenticating.',
'REQUEST_TIME_OUT' => $status = 'Timeout encountered retrieving data.',
'DATA_RETRIEVAL_FAILED' => $status = 'Login successful, but data retrieval failed.',
'PARTIAL_DATA_RETRIEVED' => $status = 'Partial data update failed.',
'PARTIAL_DATA_RETRIEVED_REM_SCHED' => $status = 'Partial data update failed.',
'SUCCESS' => $status = 'All accounts added or updated successfully.',
default => $status = false
};
if($status){
$account_status = $status;
}
match($dataset->updateEligibility ?? ''){
'ALLOW_UPDATE' => $update = 'Account connection stable.',
'ALLOW_UPDATE_WITH_CREDENTIALS' => $update = 'Please reconnect your account with updated credentials.',
'DISALLOW_UPDATE' => $update = 'Update not available due to technical issues.',
default => $update = false,
};
if($status && $update){
$account_status = $status . ' - ' . $update;
}
elseif($update){
$account_status = $update;
}
}
return [
'id' => $account->id,
'account_type' => $account->CONTAINER,
// 'account_name' => $account->accountName,
'account_name' => property_exists($account, 'accountName') ? $account->accountName : $account->nickname,
'account_status' => $account->accountStatus,
'account_status' => $account_status,
'account_number' => property_exists($account, 'accountNumber') ? '**** ' . substr($account?->accountNumber, -7) : '',
'provider_account_id' => $account->providerAccountId,
'provider_id' => $account->providerId,

View File

@ -297,4 +297,71 @@ class Yodlee
'secret' => $this->client_secret,
];
}
/**
* updateEligibility
*
* ALLOW_UPDATE
* ALLOW_UPDATE_WITH_CREDENTIALS
* DISALLOW_UPDATE
*/
/**
* additionalStatus
*
* LOGIN_IN_PROGRESS
* DATA_RETRIEVAL_IN_PROGRESS
* ACCT_SUMMARY_RECEIVED
* AVAILABLE_DATA_RETRIEVED
* PARTIAL_DATA_RETRIEVED
* DATA_RETRIEVAL_FAILED
* DATA_NOT_AVAILABLE
* ACCOUNT_LOCKED
* ADDL_AUTHENTICATION_REQUIRED
* BETA_SITE_DEV_IN_PROGRESS
* CREDENTIALS_UPDATE_NEEDED
* INCORRECT_CREDENTIALS
* PROPERTY_VALUE_NOT_AVAILABLE
* INVALID_ADDL_INFO_PROVIDED
* REQUEST_TIME_OUT
* SITE_BLOCKING_ERROR
* UNEXPECTED_SITE_ERROR
* SITE_NOT_SUPPORTED
* SITE_UNAVAILABLE
* TECH_ERROR
* USER_ACTION_NEEDED_AT_SITE
* SITE_SESSION_INVALIDATED
* NEW_AUTHENTICATION_REQUIRED
* DATASET_NOT_SUPPORTED
* ENROLLMENT_REQUIRED_FOR_DATASET
* CONSENT_REQUIRED
* CONSENT_EXPIRED
* CONSENT_REVOKED
* INCORRECT_OAUTH_TOKEN
* MIGRATION_IN_PROGRESS
*/
/**
* IN_PROGRESS LOGIN_IN_PROGRESS Provider login is in progress.
* IN_PROGRESS USER_INPUT_REQUIRED Provider site requires MFA-based authentication and needs user input for login.
* IN_PROGRESS LOGIN_SUCCESS Provider login is successful.
* IN_PROGRESS ACCOUNT_SUMMARY_RETRIEVED Account summary info may not have the complete info of accounts that are available in the provider site. This depends on the sites behaviour. Account summary info may not be available at all times.
* FAILED NEVER_INITIATED The add or update provider account was not triggered due to techincal reasons. This is a rare occurrence and usually resolves quickly.
* FAILED LOGIN_FAILED Provider login failed.
* FAILED REQUEST_TIME_OUT The process timed out.
* FAILED DATA_RETRIEVAL_FAILED All accounts under the provider account failed with same or different errors, though login was successful.
* FAILED No additional status or information will be provided when there are errors other than the ones listed above.
* PARTIAL_SUCCESS PARTIAL_DATA_RETRIEVED DATA_RETRIEVAL_FAILED_PARTIALLY One/few accounts data gathered and one/few accounts failed.
* PARTIAL_SUCCESS PARTIAL_DATA_RETRIEVED_REM_SCHED DATA_RETRIEVAL_FAILED_PARTIALLY One/few accounts data gathered One/few accounts failed
* SUCCESS All accounts under the provider was added or updated successfully.
*/
/**
* updateEligibility
*
* ALLOW_UPDATE The status indicates that the account is eligible for the next update and applies to both MFA and non-MFA accounts. For MFA-based accounts, the user may have to provide the MFA details during account refresh.
* ALLOW_UPDATE_WITH_CREDENTIALS The status indicates updating or refreshing the account by directing the user to edit the provided credentials.
* DISALLOW_UPDATE The status indicates the account is not eligible for the update or refresh process due to a site issue or a technical error.
*/
}

View File

@ -198,7 +198,7 @@ class InvoiceItemSum
private function push(): self
{
$this->sub_total += $this->getLineTotal();
$this->sub_total += round($this->getLineTotal(), $this->currency->precision);
$this->gross_sub_total += $this->getGrossLineTotal();
@ -391,15 +391,15 @@ 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);
@ -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;
}
/**

View File

@ -94,7 +94,7 @@ class InvoiceItemSumInclusive
protected RecurringInvoice | Invoice | Quote | Credit | PurchaseOrder | RecurringQuote $invoice;
private $currency;
private \App\Models\Currency $currency;
private $total_taxes;
@ -226,8 +226,10 @@ class InvoiceItemSumInclusive
$amount = $this->item->line_total - ($this->item->line_total * ($this->invoice->discount / 100));
/** @var float $item_tax_rate1_total */
$item_tax_rate1_total = $this->calcInclusiveLineTax($this->item->tax_rate1, $amount);
/** @var float $item_tax */
$item_tax += $this->formatValue($item_tax_rate1_total, $this->currency->precision);
if (strlen($this->item->tax_name1) > 1) {
@ -347,15 +349,17 @@ 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);
$item_tax += $item_tax_rate1_total;
@ -379,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);
}

View File

@ -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);

View File

@ -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();

View File

@ -33,6 +33,7 @@ class GmailTransport extends AbstractTransport
nlog("In Do Send");
$message = MessageConverter::toEmail($message->getOriginalMessage());
/** @phpstan-ignore-next-line **/
$token = $message->getHeaders()->get('gmailtoken')->getValue();
$message->getHeaders()->remove('gmailtoken');
@ -52,6 +53,8 @@ class GmailTransport extends AbstractTransport
if ($bccs) {
$bcc_list = 'Bcc: ';
/** @phpstan-ignore-next-line **/
foreach ($bccs->getAddresses() as $address) {
$bcc_list .= $address->getAddress() .',';
}

View File

@ -28,6 +28,8 @@ class Office365MailTransport extends AbstractTransport
$symfony_message = MessageConverter::toEmail($message->getOriginalMessage());
$graph = new Graph();
/** @phpstan-ignore-next-line **/
$token = $symfony_message->getHeaders()->get('gmailtoken')->getValue();
$symfony_message->getHeaders()->remove('gmailtoken');
@ -38,6 +40,8 @@ class Office365MailTransport extends AbstractTransport
$bcc_list = '';
if ($bccs) {
/** @phpstan-ignore-next-line **/
foreach ($bccs->getAddresses() as $address) {
$bcc_list .= 'Bcc: "'.$address->getAddress().'" <'.$address->getAddress().'>\r\n';
}

View File

@ -40,7 +40,7 @@ class SubscriptionCalculator
*/
public function isPaidUp() :bool
{
$outstanding_invoices_exist = Invoice::whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
$outstanding_invoices_exist = Invoice::query()->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('subscription_id', $this->invoice->subscription_id)
->where('client_id', $this->invoice->client_id)
->where('balance', '>', 0)

View File

@ -72,7 +72,7 @@ class AccountController extends BaseController
MultiDB::findAndSetDbByAccountKey($account->key);
$cu = CompanyUser::where('user_id', $account->users()->first()->id);
$cu = CompanyUser::query()->where('user_id', $account->users()->first()->id);
$company_user = $cu->first();

View File

@ -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 = [

View File

@ -11,12 +11,14 @@
namespace App\Http\Controllers\Bank;
use App\Helpers\Bank\Yodlee\DTO\AccountSummary;
use Illuminate\Http\Request;
use App\Models\BankIntegration;
use App\Helpers\Bank\Yodlee\Yodlee;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Yodlee\YodleeAuthRequest;
use App\Jobs\Bank\ProcessBankTransactions;
use App\Models\BankIntegration;
use Illuminate\Http\Request;
use App\Http\Requests\Yodlee\YodleeAuthRequest;
use App\Http\Requests\Yodlee\YodleeAdminRequest;
class YodleeController extends BaseController
{
@ -277,4 +279,27 @@ class YodleeController extends BaseController
// return response()->json(['message' => 'Unauthorized'], 403);
}
public function accountStatus(YodleeAdminRequest $request, $account_number)
{
/** @var \App\Models\User $user */
$user = auth()->user();
$bank_integration = BankIntegration::query()
->withTrashed()
->where('company_id', $user->company()->id)
->where('account_id', $account_number)
->exists();
if(!$bank_integration)
return response()->json(['message' => 'Account does not exist.'], 400);
$yodlee = new Yodlee($user->account->bank_integration_account_id);
$summary = $yodlee->getAccountSummary($account_number);
$transformed_summary = AccountSummary::from($summary[0]);
return response()->json($transformed_summary, 200);
}
}

View File

@ -71,15 +71,21 @@ class BankTransactionController extends BaseController
public function create(CreateBankTransactionRequest $request)
{
$bank_transaction = BankTransactionFactory::create(auth()->user()->company()->id, auth()->user()->id);
/** @var \App\Models\User $user */
$user = auth()->user();
$bank_transaction = BankTransactionFactory::create($user->company()->id, $user->id);
return $this->itemResponse($bank_transaction);
}
public function store(StoreBankTransactionRequest $request)
{
/** @var \App\Models\User $user */
$user = auth()->user();
//stub to store the model
$bank_transaction = $this->bank_transaction_repo->save($request->all(), BankTransactionFactory::create(auth()->user()->company()->id, auth()->user()->id));
$bank_transaction = $this->bank_transaction_repo->save($request->all(), BankTransactionFactory::create($user->company()->id, $user->id));
return $this->itemResponse($bank_transaction);
}

View File

@ -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);
}

View File

@ -530,9 +530,8 @@ class BaseController extends Controller
$paginator = $query->paginate($limit);
/** @phpstan-ignore-next-line */
$query = $paginator->getCollection(); /** @phpstan-ignore-line */
/** @phpstan-ignore-next-line **/
$query = $paginator->getCollection();
$resource = new Collection($query, $transformer, $this->entity_type);
@ -636,7 +635,7 @@ class BaseController extends Controller
$paginator = $query->paginate($limit);
/** @phpstan-ignore-next-line */
/** @phpstan-ignore-next-line **/
$query = $paginator->getCollection();
$resource = new Collection($query, $transformer, $this->entity_type);
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
@ -885,7 +884,7 @@ class BaseController extends Controller
$paginator = $query->paginate($limit);
/** @phpstan-ignore-next-line */
/** @phpstan-ignore-next-line **/
$query = $paginator->getCollection();
$resource = new Collection($query, $transformer, $this->entity_type);
@ -1066,7 +1065,7 @@ class BaseController extends Controller
$data = $this->first_load;
}
} else {
$included = request()->input('include');
$included = request()->input('include', '');
$included = explode(',', $included);
foreach ($included as $include) {

View File

@ -11,30 +11,31 @@
namespace App\Http\Controllers;
use App\Events\Client\ClientWasCreated;
use App\Events\Client\ClientWasUpdated;
use App\Utils\Ninja;
use App\Models\Client;
use App\Models\Account;
use Illuminate\Http\Response;
use App\Factory\ClientFactory;
use App\Filters\ClientFilters;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\Uploadable;
use App\Utils\Traits\BulkOptions;
use App\Jobs\Client\UpdateTaxData;
use App\Utils\Traits\SavesDocuments;
use App\Repositories\ClientRepository;
use App\Events\Client\ClientWasCreated;
use App\Events\Client\ClientWasUpdated;
use App\Transformers\ClientTransformer;
use Illuminate\Support\Facades\Storage;
use App\Http\Requests\Client\BulkClientRequest;
use App\Http\Requests\Client\CreateClientRequest;
use App\Http\Requests\Client\DestroyClientRequest;
use App\Http\Requests\Client\EditClientRequest;
use App\Http\Requests\Client\PurgeClientRequest;
use App\Http\Requests\Client\ShowClientRequest;
use App\Http\Requests\Client\PurgeClientRequest;
use App\Http\Requests\Client\StoreClientRequest;
use App\Http\Requests\Client\CreateClientRequest;
use App\Http\Requests\Client\UpdateClientRequest;
use App\Http\Requests\Client\UploadClientRequest;
use App\Models\Account;
use App\Models\Client;
use App\Repositories\ClientRepository;
use App\Transformers\ClientTransformer;
use App\Utils\Ninja;
use App\Utils\Traits\BulkOptions;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments;
use App\Utils\Traits\Uploadable;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Storage;
use App\Http\Requests\Client\DestroyClientRequest;
/**
* Class ClientController.
@ -227,7 +228,7 @@ class ClientController extends BaseController
}
if ($request->has('documents')) {
$this->saveDocuments($request->file('documents'), $client);
$this->saveDocuments($request->file('documents'), $client, $request->input('is_public', true));
}
return $this->itemResponse($client->fresh());
@ -285,4 +286,18 @@ class ClientController extends BaseController
return $this->itemResponse($merged_client);
}
/**
* Updates the client's tax data
*
* @param PurgeClientRequest $request
* @param Client $client
* @return \Illuminate\Http\JsonResponse
*/
public function updateTaxData(PurgeClientRequest $request, Client $client)
{
(new UpdateTaxData($client, $client->company))->handle();
return $this->itemResponse($client->fresh());
}
}

View File

@ -26,7 +26,8 @@ class ApplePayDomainController extends Controller
/* Self Host */
if (Ninja::isSelfHost()) {
$cgs = CompanyGateway::whereIn('gateway_key', $this->stripe_keys)
$cgs = CompanyGateway::query()
->whereIn('gateway_key', $this->stripe_keys)
->where('is_deleted', false)
->get();

View File

@ -30,7 +30,8 @@ class CreditController extends Controller
{
set_time_limit(0);
$invitation = $credit->invitations()->where('client_contact_id', auth()->user()->id)->first();
// $invitation = $credit->invitations()->where('client_contact_id', auth()->user()->id)->first();
$invitation = $credit->invitations()->where('client_contact_id', auth()->guard('contact')->user()->id)->first();
$data = [
'credit' => $credit,

View File

@ -71,7 +71,7 @@ class DocumentController extends Controller
public function downloadMultiple(DownloadMultipleDocumentsRequest $request)
{
/** @var \Illuminate\Database\Eloquent\Collection<Document> $documents **/
$documents = Document::whereIn('id', $this->transformKeys($request->file_hash))
$documents = Document::query()->whereIn('id', $this->transformKeys($request->file_hash))
->where('company_id', auth()->guard('contact')->user()->company_id)
->get();

View File

@ -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;

View File

@ -11,26 +11,27 @@
namespace App\Http\Controllers\ClientPortal;
use App\Events\Credit\CreditWasViewed;
use App\Events\Invoice\InvoiceWasViewed;
use App\Events\Misc\InvitationWasViewed;
use App\Utils\Ninja;
use App\Models\Client;
use App\Models\Payment;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use App\Models\ClientContact;
use App\Models\QuoteInvitation;
use App\Utils\Traits\MakesHash;
use App\Models\CreditInvitation;
use App\Utils\Traits\MakesDates;
use App\Jobs\Entity\CreateRawPdf;
use App\Models\InvoiceInvitation;
use App\Events\Quote\QuoteWasViewed;
use App\Http\Controllers\Controller;
use App\Jobs\Entity\CreateRawPdf;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\CreditInvitation;
use App\Models\InvoiceInvitation;
use App\Models\Payment;
use App\Models\PurchaseOrderInvitation;
use App\Models\QuoteInvitation;
use App\Services\ClientPortal\InstantPayment;
use App\Utils\Ninja;
use App\Utils\Traits\MakesDates;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use App\Events\Credit\CreditWasViewed;
use App\Events\Contact\ContactLoggedIn;
use App\Models\PurchaseOrderInvitation;
use App\Events\Invoice\InvoiceWasViewed;
use App\Events\Misc\InvitationWasViewed;
use App\Services\ClientPortal\InstantPayment;
/**
* Class InvitationController.
@ -94,6 +95,7 @@ class InvitationController extends Controller
}
$client_contact = $invitation->contact;
event(new ContactLoggedIn($client_contact, $client_contact->company, Ninja::eventVars()));
if (empty($client_contact->email)) {
$client_contact->email = Str::random(15) . "@example.com";

View File

@ -87,14 +87,19 @@ class InvoiceController extends Controller
public function showBlob($hash)
{
$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']),
};
if (! $invitation) {
return redirect('/');
}
$file = (new \App\Jobs\Entity\CreateRawPdf($invitation, $invitation->company->db))->handle();
$headers = ['Content-Type' => 'application/pdf'];
@ -128,7 +133,8 @@ class InvoiceController extends Controller
public function downloadInvoices($ids)
{
$data['invoices'] = Invoice::whereIn('id', $ids)
$data['invoices'] = Invoice::query()
->whereIn('id', $ids)
->whereClientId(auth()->guard('contact')->user()->client->id)
->withTrashed()
->get();
@ -153,7 +159,8 @@ class InvoiceController extends Controller
*/
private function makePayment(array $ids)
{
$invoices = Invoice::whereIn('id', $ids)
$invoices = Invoice::query()
->whereIn('id', $ids)
->whereClientId(auth()->guard('contact')->user()->client->id)
->withTrashed()
->get();
@ -215,7 +222,8 @@ class InvoiceController extends Controller
*/
private function downloadInvoicePDF(array $ids)
{
$invoices = Invoice::whereIn('id', $ids)
$invoices = Invoice::query()
->whereIn('id', $ids)
->withTrashed()
->whereClientId(auth()->guard('contact')->user()->client->id)
->get();
@ -244,11 +252,20 @@ class InvoiceController extends Controller
// create new archive
$zipFile = new \PhpZip\ZipFile();
try {
foreach ($invoices as $invoice) {
//add it to the zip
$zipFile->addFromString(basename($invoice->pdf_file_path()), file_get_contents($invoice->pdf_file_path(null, 'url', true)));
if ($invoice->client->getSetting('enable_e_invoice')) {
$xml = $invoice->service()->getEInvoice();
$zipFile->addFromString($invoice->getFileName("xml"), $xml);
}
$file = $invoice->service()->getRawInvoicePdf();
$zip_file_name = $invoice->getFileName();
$zipFile->addFromString($zip_file_name, $file);
}
$filename = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.invoices')).'.zip';
$filepath = sys_get_temp_dir().'/'.$filename;

View File

@ -40,13 +40,13 @@ class NinjaPlanController extends Controller
public function index(string $contact_key, string $account_or_company_key)
{
MultiDB::findAndSetDbByCompanyKey($account_or_company_key);
$company = Company::where('company_key', $account_or_company_key)->first();
$company = Company::query()->where('company_key', $account_or_company_key)->first();
if (! $company) {
MultiDB::findAndSetDbByAccountKey($account_or_company_key);
/** @var \App\Models\Account $account **/
$account = Account::where('key', $account_or_company_key)->first();
$account = Account::query()->where('key', $account_or_company_key)->first();
} else {
$account = $company->account;
}
@ -157,6 +157,8 @@ class NinjaPlanController extends Controller
//create recurring invoice
$subscription_repo = new SubscriptionRepository();
/** @var \App\Models\Subscription $subscription **/
$subscription = Subscription::find(6);
$recurring_invoice = RecurringInvoiceFactory::create($subscription->company_id, $subscription->user_id);
@ -181,7 +183,7 @@ class NinjaPlanController extends Controller
->increment()
->queue();
$old_recurring = RecurringInvoice::where('company_id', config('ninja.ninja_default_company_id'))
$old_recurring = RecurringInvoice::query()->where('company_id', config('ninja.ninja_default_company_id'))
->where('client_id', $client->id)
->where('id', '!=', $recurring_invoice->id)
->first();
@ -215,7 +217,7 @@ class NinjaPlanController extends Controller
$data['late_invoice'] = false;
if (MultiDB::findAndSetDbByAccountKey(Auth::guard('contact')->user()->client->custom_value2)) {
$account = Account::where('key', Auth::guard('contact')->user()->client->custom_value2)->first();
$account = Account::query()->where('key', Auth::guard('contact')->user()->client->custom_value2)->first();
if ($account) {
//offer the option to have a free trial

View File

@ -169,7 +169,7 @@ class PaymentController extends Controller
$payment = $payment->service()->applyCredits($payment_hash)->save();
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Invoice> $invoices */
$invoices = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')));
$invoices = Invoice::query()->whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')));
$invoices->each(function ($invoice) {
/** @var \App\Models\Invoice $invoice **/

View File

@ -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
{
@ -53,7 +54,7 @@ class QuoteController extends Controller
{
/* If the quote is expired, convert the status here */
$invitation = $quote->invitations()->where('client_contact_id', auth()->user()->id)->first();
$invitation = $quote->invitations()->where('client_contact_id', auth()->guard('contact')->user()->id)->first();
$data = [
'quote' => $quote,
@ -95,8 +96,9 @@ class QuoteController extends Controller
/** @var \App\Models\ClientContact $client_contact **/
$client_contact = auth()->user();
$data['quotes'] = Quote::whereIn('id', $ids)
->whereClientId($client_contact->client->id)
$data['quotes'] = Quote::query()
->whereIn('id', $ids)
->where('client_id', $client_contact->client_id)
->withTrashed()
->get();
@ -120,36 +122,38 @@ class QuoteController extends Controller
/** @var \App\Models\ClientContact $client_contact **/
$client_contact = auth()->user();
$quotes = Quote::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';
@ -160,7 +164,6 @@ class QuoteController extends Controller
return response()->download($filepath, $filename)->deleteFileAfterSend(true);
} catch (\PhpZip\Exception\ZipException $e) {
// handle exception
} finally {
$zipFile->close();
}
@ -168,7 +171,8 @@ class QuoteController extends Controller
protected function approve(array $ids, $process = false)
{
$quotes = Quote::whereIn('id', $ids)
$quotes = Quote::query()
->whereIn('id', $ids)
->where('client_id', auth()->guard('contact')->user()->client->id)
->where('company_id', auth()->guard('contact')->user()->client->company_id)
->whereIn('status_id', [Quote::STATUS_DRAFT, Quote::STATUS_SENT])
@ -195,10 +199,10 @@ class QuoteController extends Controller
}
}
if (count($ids) == 1) {
if ($quotes->count() == 1) {
//forward client to the invoice if it exists
if ($quote->invoice()->exists()) {
return redirect()->route('client.invoice.show', $quote->invoice->hashed_id);
if ($quotes->first()->invoice()->exists()) {
return redirect()->route('client.invoice.show', $quotes->first()->invoice->hashed_id);
}
return redirect()->route('client.quote.show', $quotes->first()->hashed_id);

View File

@ -22,9 +22,10 @@ class SwitchCompanyController extends Controller
public function __invoke(string $contact)
{
$client_contact = ClientContact::where('email', auth()->user()->email)
$client_contact = ClientContact::query()
->where('email', auth()->user()->email)
->where('id', $this->transformKeys($contact))
->first();
->firstOrFail();
auth()->guard('contact')->loginUsingId($client_contact->id, true);

View File

@ -34,7 +34,7 @@ class UploadController extends Controller
/** @var \App\Models\ClientContact $client_contact **/
$client_contact = auth()->user();
$this->saveDocuments($request->getFile(), $client_contact->client, true);
$this->saveDocuments($request->getFile(), $client_contact->client, $request->input('is_public', true));
return response([], 200);
}

View File

@ -11,38 +11,39 @@
namespace App\Http\Controllers;
use App\DataMapper\Analytics\AccountDeleted;
use App\DataMapper\CompanySettings;
use App\Http\Requests\Company\CreateCompanyRequest;
use App\Http\Requests\Company\DefaultCompanyRequest;
use App\Http\Requests\Company\DestroyCompanyRequest;
use App\Http\Requests\Company\EditCompanyRequest;
use App\Http\Requests\Company\ShowCompanyRequest;
use App\Http\Requests\Company\StoreCompanyRequest;
use App\Http\Requests\Company\UpdateCompanyRequest;
use App\Http\Requests\Company\UploadCompanyRequest;
use App\Jobs\Company\CreateCompany;
use App\Jobs\Company\CreateCompanyPaymentTerms;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Jobs\Company\CreateCompanyToken;
use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Mail\Company\CompanyDeleted;
use Str;
use App\Utils\Ninja;
use App\Models\Account;
use App\Models\Company;
use App\Models\CompanyUser;
use App\Repositories\CompanyRepository;
use App\Transformers\CompanyTransformer;
use App\Transformers\CompanyUserTransformer;
use App\Utils\Ninja;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments;
use App\Utils\Traits\Uploadable;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Storage;
use Str;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\Uploadable;
use App\Jobs\Mail\NinjaMailerJob;
use App\DataMapper\CompanySettings;
use App\Jobs\Company\CreateCompany;
use App\Jobs\Company\CompanyTaxRate;
use App\Jobs\Mail\NinjaMailerObject;
use App\Mail\Company\CompanyDeleted;
use App\Utils\Traits\SavesDocuments;
use Turbo124\Beacon\Facades\LightLogs;
use App\Repositories\CompanyRepository;
use Illuminate\Support\Facades\Storage;
use App\Jobs\Company\CreateCompanyToken;
use App\Transformers\CompanyTransformer;
use App\DataMapper\Analytics\AccountDeleted;
use App\Transformers\CompanyUserTransformer;
use Illuminate\Foundation\Bus\DispatchesJobs;
use App\Jobs\Company\CreateCompanyPaymentTerms;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Http\Requests\Company\EditCompanyRequest;
use App\Http\Requests\Company\ShowCompanyRequest;
use App\Http\Requests\Company\StoreCompanyRequest;
use App\Http\Requests\Company\CreateCompanyRequest;
use App\Http\Requests\Company\UpdateCompanyRequest;
use App\Http\Requests\Company\UploadCompanyRequest;
use App\Http\Requests\Company\DefaultCompanyRequest;
use App\Http\Requests\Company\DestroyCompanyRequest;
/**
* Class CompanyController.
@ -423,7 +424,7 @@ class CompanyController extends BaseController
$company = $this->company_repo->save($request->all(), $company);
if ($request->has('documents')) {
$this->saveDocuments($request->input('documents'), $company, false);
$this->saveDocuments($request->input('documents'), $company, $request->input('is_public', true));
}
if($request->has('e_invoice_certificate') && !is_null($request->file("e_invoice_certificate"))){
@ -615,7 +616,7 @@ class CompanyController extends BaseController
}
if ($request->has('documents')) {
$this->saveDocuments($request->file('documents'), $company);
$this->saveDocuments($request->file('documents'), $company, $request->input('is_public', true));
}
return $this->itemResponse($company->fresh());
@ -679,4 +680,36 @@ class CompanyController extends BaseController
return $this->itemResponse($company->fresh());
}
public function updateOriginTaxData(DefaultCompanyRequest $request, Company $company)
{
if($company->settings->country_id == "840" && !$company?->account->isFreeHostedClient())
{
try {
(new CompanyTaxRate($company))->handle();
} catch(\Exception $e) {
return response()->json(['message' => 'There was a problem updating the tax rates. Please try again.'], 400);
}
}
else
return response()->json(['message' => 'Tax configuration not available due to settings / plan restriction.'], 400);
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);
}
}

View File

@ -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();

View File

@ -105,7 +105,7 @@ class ConnectedAccountController extends BaseController
->execute();
if ($user) {
$email = $user->getMail() ?: $user->getUserPrincipalName();
$email = $user->getUserPrincipalName() ?? false;
nlog("microsoft");
nlog($email);

View File

@ -148,10 +148,10 @@ class CreditController extends BaseController
*/
public function create(CreateCreditRequest $request)
{
/** @var \App\Models\User $user**/
/** @var \App\Models\User $user **/
$user = auth()->user();
$credit = CreditFactory::create($user->company()->id, auth()->user()->id);
$credit = CreditFactory::create($user->company()->id, $user->id);
return $this->itemResponse($credit);
}
@ -197,11 +197,9 @@ class CreditController extends BaseController
public function store(StoreCreditRequest $request)
{
/** @var \App\Models\User $user**/
/** @var \App\Models\User $user **/
$user = auth()->user();
// $client = Client::find($request->input('client_id'));
$credit = $this->credit_repository->save($request->all(), CreditFactory::create($user->company()->id, $user->id));
$credit = $credit->service()
@ -387,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();
@ -506,7 +504,7 @@ class CreditController extends BaseController
public function bulk(BulkCreditRequest $request)
{
/** @var \App\Models\User $user**/
/** @var \App\Models\User $user **/
$user = auth()->user();
$action = $request->input('action');
@ -531,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();
@ -594,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']);
@ -725,7 +718,7 @@ class CreditController extends BaseController
* Update the specified resource in storage.
*
* @param UploadCreditRequest $request
* @param Credit $client
* @param Credit $credit
* @return Response
*
*
@ -778,7 +771,7 @@ class CreditController extends BaseController
}
if ($request->has('documents')) {
$this->saveDocuments($request->file('documents'), $credit);
$this->saveDocuments($request->file('documents'), $credit, $request->input('is_public', true));
}
return $this->itemResponse($credit->fresh());

View File

@ -128,7 +128,7 @@ class DocumentController extends BaseController
/**
* Show the form for editing the specified resource.
*
* @param EditDocumentRegquest $request
* @param EditDocumentRequest $request
* @param Document $document
* @return Response
*/

View File

@ -78,6 +78,8 @@ class EmailController extends BaseController
$entity_obj->service()->markSent()->save();
$mo->invitation_id = $invitation->id;
$mo->client_id = $invitation->contact->client_id ?? null;
$mo->vendor_id = $invitation->contact->vendor_id ?? null;
Email::dispatch($mo, $invitation->company);
}
@ -125,7 +127,6 @@ class EmailController extends BaseController
$this->entity_transformer = PurchaseOrderTransformer::class;
}
// @phpstan-ignore-next-line
return $this->itemResponse($entity_obj->fresh());
}

View File

@ -0,0 +1,68 @@
<?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\Http\Controllers;
use App\Http\Requests\Email\ClientEmailHistoryRequest;
use App\Http\Requests\Email\EntityEmailHistoryRequest;
use App\Models\Client;
use App\Models\SystemLog;
use App\Utils\Traits\MakesHash;
class EmailHistoryController extends BaseController
{
use MakesHash;
public function __construct()
{
}
public function clientHistory(ClientEmailHistoryRequest $request, Client $client)
{
$data = SystemLog::where('client_id', $client->id)
->where('category_id', SystemLog::CATEGORY_MAIL)
->orderBy('id', 'DESC')
->cursor()
->map(function ($system_log) {
if($system_log->log['history'] ?? false) {
return $system_log->log['history'];
}
});
return response()->json($data, 200);
}
/**
* May need to expand on this using
* just the message-id and search for the
* entity in the invitations
*/
public function entityHistory(EntityEmailHistoryRequest $request)
{
/** @var \App\Models\User $user */
$user = auth()->user();
$data = SystemLog::where('company_id', $user->company()->id)
->where('category_id', SystemLog::CATEGORY_MAIL)
->whereJsonContains('log->history->entity_id', $this->encodePrimaryKey($request->entity_id))
->orderBy('id', 'DESC')
->cursor()
->map(function ($system_log) {
if($system_log->log['history'] ?? false) {
return $system_log->log['history'];
}
});
return response()->json($data, 200);
}
}

View File

@ -564,7 +564,7 @@ class ExpenseController extends BaseController
}
if ($request->has('documents')) {
$this->saveDocuments($request->file('documents'), $expense);
$this->saveDocuments($request->file('documents'), $expense, $request->input('is_public', true));
}
return $this->itemResponse($expense->fresh());

View File

@ -139,12 +139,14 @@ class GroupSettingController extends BaseController
*/
public function update(UpdateGroupSettingRequest $request, GroupSetting $group_setting)
{
/** Need this to prevent settings from being overwritten */
if(!$request->file('company_logo'))
$group_setting = $this->group_setting_repo->save($request->all(), $group_setting);
$this->uploadLogo($request->file('company_logo'), $group_setting->company, $group_setting);
if ($request->has('documents')) {
$this->saveDocuments($request->input('documents'), $group_setting, false);
$this->saveDocuments($request->input('documents'), $group_setting, $request->input('is_public', true));
}
return $this->itemResponse($group_setting);
@ -217,7 +219,7 @@ class GroupSettingController extends BaseController
}
if ($request->has('documents')) {
$this->saveDocuments($request->file('documents'), $group_setting);
$this->saveDocuments($request->file('documents'), $group_setting, $request->input('is_public', true));
}
return $this->itemResponse($group_setting->fresh());

View File

@ -92,15 +92,82 @@ class ImportController extends Controller
$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);
}

View File

@ -162,7 +162,9 @@ class InvoiceController extends BaseController
*/
public function create(CreateInvoiceRequest $request)
{
$invoice = InvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id);
/** @var \App\Models\User $user */
$user = auth()->user();
$invoice = InvoiceFactory::create($user->company()->id, $user->id);
return $this->itemResponse($invoice);
}
@ -211,7 +213,11 @@ class InvoiceController extends BaseController
*/
public function store(StoreInvoiceRequest $request)
{
$invoice = $this->invoice_repo->save($request->all(), InvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id));
/** @var \App\Models\User $user */
$user = auth()->user();
$invoice = $this->invoice_repo->save($request->all(), InvoiceFactory::create($user->company()->id, $user->id));
$invoice = $invoice->service()
->fillDefaults()
@ -219,7 +225,7 @@ class InvoiceController extends BaseController
->adjustInventory()
->save();
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars($user ? $user->id : null)));
$transaction = [
'invoice' => $invoice->transaction_event(),
@ -409,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)));
@ -473,62 +479,17 @@ class InvoiceController extends BaseController
return $this->itemResponse($invoice->fresh());
}
/**
* Perform bulk actions on the list view.
*
* @return \Illuminate\Support\Collection
*
* @OA\Post(
* path="/api/v1/invoices/bulk",
* operationId="bulkInvoices",
* tags={"invoices"},
* summary="Performs bulk actions on an array of invoices",
* description="",
* @OA\Parameter(ref="#/components/parameters/X-API-TOKEN"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/index"),
* @OA\RequestBody(
* description="User credentials",
* required=true,
* @OA\MediaType(
* mediaType="application/json",
* @OA\Schema(
* type="array",
* @OA\Items(
* type="integer",
* description="Array of hashed IDs to be bulk 'actioned",
* example="[0,1,2,3]",
* ),
* )
* )
* ),
* @OA\Response(
* response=200,
* description="The Bulk Action response",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function bulk(BulkInvoiceRequest $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);
}
@ -543,8 +504,8 @@ class InvoiceController extends BaseController
*/
if ($action == 'bulk_download' && $invoices->count() > 1) {
$invoices->each(function ($invoice) {
if (auth()->user()->cannot('view', $invoice)) {
$invoices->each(function ($invoice) use($user) {
if ($user->cannot('view', $invoice)) {
nlog('access denied');
return response()->json(['message' => ctrans('text.access_denied')]);
@ -556,7 +517,7 @@ class InvoiceController extends BaseController
return response()->json(['message' => ctrans('texts.sent_message')], 200);
}
if ($action == 'download' && $invoices->count() >=1 && auth()->user()->can('view', $invoices->first())) {
if ($action == 'download' && $invoices->count() >=1 && $user->can('view', $invoices->first())) {
$file = $invoices->first()->service()->getInvoicePdf();
return response()->streamDownload(function () use ($file) {
@ -564,9 +525,9 @@ class InvoiceController extends BaseController
}, basename($file), ['Content-Type' => 'application/pdf']);
}
if ($action == 'bulk_print' && auth()->user()->can('view', $invoices->first())) {
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();
@ -579,15 +540,15 @@ class InvoiceController extends BaseController
/*
* Send the other actions to the switch
*/
$invoices->each(function ($invoice, $key) use ($action) {
if (auth()->user()->can('edit', $invoice)) {
$invoices->each(function ($invoice, $key) use ($action, $user) {
if ($user->can('edit', $invoice)) {
$this->performAction($invoice, $action, true);
}
});
/* Need to understand which permission are required for the given bulk action ie. view / edit */
return $this->listResponse(Invoice::withTrashed()->whereIn('id', $this->transformKeys($ids))->company());
return $this->listResponse(Invoice::query()->withTrashed()->whereIn('id', $this->transformKeys($ids))->company());
}
/**
@ -739,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);
}
@ -889,6 +850,7 @@ class InvoiceController extends BaseController
$invoice = $invitation->invoice;
$file = $invoice->service()->getEInvoice($contact);
$file_name = $invoice->getFileName("xml");
$headers = ['Content-Type' => 'application/xml'];
@ -897,8 +859,8 @@ class InvoiceController extends BaseController
}
return response()->streamDownload(function () use ($file) {
echo Storage::get($file);
}, basename($file), $headers);
echo $file;
}, $file_name, $headers);
}
/**
@ -1005,16 +967,17 @@ class InvoiceController extends BaseController
*/
public function upload(UploadInvoiceRequest $request, Invoice $invoice)
{
if (! $this->checkFeature(Account::FEATURE_DOCUMENTS)) {
return $this->featureFailure();
}
if ($request->has('documents')) {
$this->saveDocuments($request->file('documents'), $invoice);
$this->saveDocuments($request->file('documents'), $invoice, $request->input('is_public', true));
}
if ($request->has('file')) {
$this->saveDocuments($request->file('documents'), $invoice);
$this->saveDocuments($request->file('file'), $invoice, $request->input('is_public', true));
}
return $this->itemResponse($invoice->fresh());
@ -1022,7 +985,10 @@ class InvoiceController extends BaseController
public function update_reminders(UpdateReminderRequest $request)
{
UpdateReminders::dispatch(auth()->user()->company());
/** @var \App\Models\User $user */
$user = auth()->user();
UpdateReminders::dispatch($user->company());
return response()->json(['message' => 'Updating reminders'], 200);
}

View File

@ -301,6 +301,7 @@ class MigrationController extends BaseController
$user = auth()->user();
$company_count = $user->account->companies()->count();
$fresh_company = false;
// Look for possible existing company (based on company keys).
$existing_company = Company::whereRaw('BINARY `company_key` = ?', [$company['company_key']])->first();

View File

@ -74,6 +74,7 @@ class OneTimeTokenController extends BaseController
'user_id' => $user->id,
'company_key'=> $user->company()->company_key,
'context' => $request->input('context'),
'is_react' => $request->request()->hasHeader('X-REACT') ? true : false,
];
Cache::put($hash, $data, 3600);

View File

@ -749,7 +749,7 @@ class PaymentController extends BaseController
}
if ($request->has('documents')) {
$this->saveDocuments($request->file('documents'), $payment);
$this->saveDocuments($request->file('documents'), $payment, $request->input('is_public', true));
}
return $this->itemResponse($payment->fresh());

View File

@ -95,7 +95,7 @@ class PreviewPurchaseOrderController extends BaseController
return response()->json(['message' => ctrans('texts.invalid_design_object')], 400);
}
$entity_obj = PurchaseOrder::whereId($this->decodePrimaryKey(request()->input('entity_id')))->company()->first();
$entity_obj = PurchaseOrder::query()->whereId($this->decodePrimaryKey(request()->input('entity_id')))->company()->first();
if (! $entity_obj) {
return $this->blankEntity();
@ -181,7 +181,8 @@ class PreviewPurchaseOrderController extends BaseController
DB::connection(config('database.default'))->beginTransaction();
if ($request->has('entity_id')) {
$entity_obj = $class::on(config('database.default'))
/** @var \App\Models\PurchaseOrder|\Illuminate\Contracts\Database\Eloquent\Builder $entity_obj **/
$entity_obj = \App\Models\PurchaseOrder::on(config('database.default'))
->with('vendor.company')
->where('id', $this->decodePrimaryKey($request->input('entity_id')))
->where('company_id', $company->id)

View File

@ -541,7 +541,7 @@ class ProductController extends BaseController
}
if ($request->has('documents')) {
$this->saveDocuments($request->file('documents'), $product);
$this->saveDocuments($request->file('documents'), $product, $request->input('is_public', true));
}
return $this->itemResponse($product->fresh());

View File

@ -264,7 +264,7 @@ class ProjectController extends BaseController
$project->saveQuietly();
if ($request->has('documents')) {
$this->saveDocuments($request->input('documents'), $project);
$this->saveDocuments($request->input('documents'), $project, $request->input('is_public', true));
}
event('eloquent.updated: App\Models\Project', $project);
@ -373,7 +373,7 @@ class ProjectController extends BaseController
}
if ($request->has('documents')) {
$this->saveDocuments($request->input('documents'), $project);
$this->saveDocuments($request->input('documents'), $project, $request->input('is_public', true));
}
event('eloquent.created: App\Models\Project', $project);
@ -565,7 +565,7 @@ class ProjectController extends BaseController
}
if ($request->has('documents')) {
$this->saveDocuments($request->file('documents'), $project);
$this->saveDocuments($request->file('documents'), $project, $request->input('is_public', true));
}
return $this->itemResponse($project->fresh());

View File

@ -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);
}
});
@ -751,7 +760,7 @@ class PurchaseOrderController extends BaseController
}
if ($request->has('documents')) {
$this->saveDocuments($request->file('documents'), $purchase_order);
$this->saveDocuments($request->file('documents'), $purchase_order, $request->input('is_public', true));
}
return $this->itemResponse($purchase_order->fresh());

View File

@ -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,6 +680,13 @@ class QuoteController extends BaseController
private function performAction(Quote $quote, $action, $bulk = false)
{
switch ($action) {
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':
@ -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();
@ -905,7 +898,7 @@ class QuoteController extends BaseController
}
if ($request->has('documents')) {
$this->saveDocuments($request->file('documents'), $quote);
$this->saveDocuments($request->file('documents'), $quote, $request->input('is_public', true));
}
return $this->itemResponse($quote->fresh());

View File

@ -609,7 +609,7 @@ class RecurringExpenseController extends BaseController
}
if ($request->has('documents')) {
$this->saveDocuments($request->file('documents'), $recurring_expense);
$this->saveDocuments($request->file('documents'), $recurring_expense, $request->input('is_public', true));
}
return $this->itemResponse($recurring_expense->fresh());

View File

@ -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);
}
});
@ -550,7 +559,7 @@ class RecurringInvoiceController extends BaseController
}
if ($request->has('documents')) {
$this->saveDocuments($request->file('documents'), $recurring_invoice);
$this->saveDocuments($request->file('documents'), $recurring_invoice, $request->input('is_public', true));
}
return $this->itemResponse($recurring_invoice->fresh());

View File

@ -14,6 +14,7 @@ namespace App\Http\Controllers\Reports;
use App\Utils\Traits\MakesHash;
use App\Jobs\Report\SendToAdmin;
use App\Export\CSV\ActivityExport;
use App\Jobs\Report\PreviewReport;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Report\GenericReportRequest;
@ -31,14 +32,27 @@ class ActivityReportController 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(), ActivityExport::class, $this->filename);
SendToAdmin::dispatch($user->company(), $request->all(), ActivityExport::class, $this->filename);
return response()->json(['message' => 'working...'], 200);
}
// expect a list of visible fields, or use the default
$export = new ActivityExport(auth()->user()->company(), $request->all());
if($request->has('output') && $request->input('output') == 'json') {
$hash = \Illuminate\Support\Str::uuid();
PreviewReport::dispatch($user->company(), $request->all(), ActivityExport::class, $hash);
return response()->json(['message' => $hash], 200);
}
$export = new ActivityExport($user->company(), $request->all());
$csv = $export->run();

View File

@ -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\ContactExport;
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 ClientContactReportController extends BaseController
{
@ -62,13 +63,29 @@ class ClientContactReportController 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(), ContactExport::class, $this->filename);
SendToAdmin::dispatch($user->company(), $request->all(), ContactExport::class, $this->filename);
return response()->json(['message' => 'working...'], 200);
}
// expect a list of visible fields, or use the default
$export = new ContactExport(auth()->user()->company(), $request->all());
if($request->has('output') && $request->input('output') == 'json') {
$hash = \Illuminate\Support\Str::uuid();
PreviewReport::dispatch($user->company(), $request->all(), ContactExport::class, $hash);
return response()->json(['message' => $hash], 200);
}
// expect a list of visible fields, or use the default
$export = new ContactExport($user->company(), $request->all());
$csv = $export->run();

Some files were not shown because too many files have changed in this diff Show More