merge v5-dev

This commit is contained in:
David Bomba 2022-07-17 20:38:00 +10:00
commit 9b6bfbf96c
18 changed files with 186033 additions and 186186 deletions

View File

@ -91,38 +91,38 @@ class CheckData extends Command
protected $wrong_paid_status = 0; protected $wrong_paid_status = 0;
public function handle() public function handle()
{ {
$time_start = microtime(true); $time_start = microtime(true);
$database_connection = $this->option('database') ? $this->option('database') : 'Connected to Default DB'; $database_connection = $this->option('database') ? $this->option('database') : 'Connected to Default DB';
$fix_status = $this->option('fix') ? 'Fixing Issues' : 'Just checking issues '; $fix_status = $this->option('fix') ? "Fixing Issues" : "Just checking issues ";
$this->logMessage(date('Y-m-d h:i:s').' Running CheckData... on '.$database_connection." Fix Status = {$fix_status}"); $this->logMessage(date('Y-m-d h:i:s').' Running CheckData... on ' . $database_connection . " Fix Status = {$fix_status}");
if ($database = $this->option('database')) { if ($database = $this->option('database')) {
config(['database.default' => $database]); config(['database.default' => $database]);
} }
$this->checkInvoiceBalances(); $this->checkInvoiceBalances();
$this->checkClientBalanceEdgeCases();
$this->checkPaidToDatesNew(); $this->checkPaidToDatesNew();
$this->checkContacts(); $this->checkContacts();
$this->checkVendorContacts(); $this->checkVendorContacts();
$this->checkEntityInvitations(); $this->checkEntityInvitations();
$this->checkCompanyData(); $this->checkCompanyData();
$this->checkBalanceVsPaidStatus(); $this->checkBalanceVsPaidStatus();
if (Ninja::isHosted()) { if(Ninja::isHosted())
$this->checkAccountStatuses(); $this->checkAccountStatuses();
}
if (! $this->option('client_id')) { if (! $this->option('client_id')) {
$this->checkOAuth(); $this->checkOAuth();
} }
$this->logMessage('Done: '.strtoupper($this->isValid ? Account::RESULT_SUCCESS : Account::RESULT_FAILURE)); $this->logMessage('Done: '.strtoupper($this->isValid ? Account::RESULT_SUCCESS : Account::RESULT_FAILURE));
$this->logMessage('Total execution time in seconds: '.(microtime(true) - $time_start)); $this->logMessage('Total execution time in seconds: ' . (microtime(true) - $time_start));
$errorEmail = config('ninja.error_email'); $errorEmail = config('ninja.error_email');
@ -235,7 +235,7 @@ class CheckData extends Command
if ($this->option('fix') == 'true') { if ($this->option('fix') == 'true') {
foreach ($clients as $client) { foreach ($clients as $client) {
$this->logMessage("Fixing missing contacts #{$client->id}"); $this->logMessage("Fixing missing contacts #{$client->id}");
$new_contact = ClientContactFactory::create($client->company_id, $client->user_id); $new_contact = ClientContactFactory::create($client->company_id, $client->user_id);
$new_contact->client_id = $client->id; $new_contact->client_id = $client->id;
$new_contact->contact_key = Str::random(40); $new_contact->contact_key = Str::random(40);
@ -243,6 +243,7 @@ class CheckData extends Command
$new_contact->save(); $new_contact->save();
} }
} }
} }
private function checkVendorContacts() private function checkVendorContacts()
@ -290,11 +291,12 @@ class CheckData extends Command
} }
if ($this->option('fix') == 'true') { if ($this->option('fix') == 'true') {
$vendors = Vendor::withTrashed()->doesntHave('contacts')->get(); $vendors = Vendor::withTrashed()->doesntHave('contacts')->get();
foreach ($vendors as $vendor) { foreach ($vendors as $vendor) {
$this->logMessage("Fixing missing vendor contacts #{$vendor->id}"); $this->logMessage("Fixing missing vendor contacts #{$vendor->id}");
$new_contact = VendorContactFactory::create($vendor->company_id, $vendor->user_id); $new_contact = VendorContactFactory::create($vendor->company_id, $vendor->user_id);
$new_contact->vendor_id = $vendor->id; $new_contact->vendor_id = $vendor->id;
$new_contact->contact_key = Str::random(40); $new_contact->contact_key = Str::random(40);
@ -302,8 +304,10 @@ class CheckData extends Command
$new_contact->save(); $new_contact->save();
} }
} }
} }
private function checkFailedJobs() private function checkFailedJobs()
{ {
if (config('ninja.testvars.travis')) { if (config('ninja.testvars.travis')) {
@ -352,32 +356,36 @@ class CheckData extends Command
private function checkEntityInvitations() private function checkEntityInvitations()
{ {
RecurringInvoiceInvitation::where('deleted_at', '0000-00-00 00:00:00.000000')->withTrashed()->update(['deleted_at' => null]);
InvoiceInvitation::where('deleted_at', '0000-00-00 00:00:00.000000')->withTrashed()->update(['deleted_at' => null]); RecurringInvoiceInvitation::where('deleted_at',"0000-00-00 00:00:00.000000")->withTrashed()->update(['deleted_at' => null]);
QuoteInvitation::where('deleted_at', '0000-00-00 00:00:00.000000')->withTrashed()->update(['deleted_at' => null]); InvoiceInvitation::where('deleted_at',"0000-00-00 00:00:00.000000")->withTrashed()->update(['deleted_at' => null]);
CreditInvitation::where('deleted_at', '0000-00-00 00:00:00.000000')->withTrashed()->update(['deleted_at' => null]); QuoteInvitation::where('deleted_at',"0000-00-00 00:00:00.000000")->withTrashed()->update(['deleted_at' => null]);
CreditInvitation::where('deleted_at',"0000-00-00 00:00:00.000000")->withTrashed()->update(['deleted_at' => null]);
$entities = ['invoice', 'quote', 'credit', 'recurring_invoice']; $entities = ['invoice', 'quote', 'credit', 'recurring_invoice'];
foreach ($entities as $entity) { foreach($entities as $entity)
{
$table = "{$entity}s"; $table = "{$entity}s";
$invitation_table = "{$entity}_invitations"; $invitation_table = "{$entity}_invitations";
$entities = DB::table($table) $entities = DB::table($table)
->leftJoin($invitation_table, function ($join) use ($invitation_table, $table, $entity) { ->leftJoin($invitation_table, function ($join) use($invitation_table, $table, $entity){
$join->on("{$invitation_table}.{$entity}_id", '=', "{$table}.id"); $join->on("{$invitation_table}.{$entity}_id", '=', "{$table}.id");
// ->whereNull("{$invitation_table}.deleted_at"); // ->whereNull("{$invitation_table}.deleted_at");
}) })
->groupBy("{$table}.id", "{$table}.user_id", "{$table}.company_id", "{$table}.client_id") ->groupBy("{$table}.id", "{$table}.user_id", "{$table}.company_id", "{$table}.client_id")
->havingRaw("count({$invitation_table}.id) = 0") ->havingRaw("count({$invitation_table}.id) = 0")
->get(["{$table}.id", "{$table}.user_id", "{$table}.company_id", "{$table}.client_id"]); ->get(["{$table}.id", "{$table}.user_id", "{$table}.company_id", "{$table}.client_id"]);
$this->logMessage($entities->count()." {$table} without any invitations");
if ($this->option('fix') == 'true') { $this->logMessage($entities->count()." {$table} without any invitations");
$this->fixInvitations($entities, $entity);
} if ($this->option('fix') == 'true')
$this->fixInvitations($entities, $entity);
} }
} }
private function fixInvitations($entities, $entity) private function fixInvitations($entities, $entity)
@ -386,7 +394,8 @@ class CheckData extends Command
$entity_obj = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation'; $entity_obj = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation';
foreach ($entities as $entity) { foreach($entities as $entity)
{
$invitation = new $entity_obj(); $invitation = new $entity_obj();
$invitation->company_id = $entity->company_id; $invitation->company_id = $entity->company_id;
$invitation->user_id = $entity->user_id; $invitation->user_id = $entity->user_id;
@ -394,17 +403,20 @@ class CheckData extends Command
$invitation->client_contact_id = ClientContact::whereClientId($entity->client_id)->first()->id; $invitation->client_contact_id = ClientContact::whereClientId($entity->client_id)->first()->id;
$invitation->key = Str::random(config('ninja.key_length')); $invitation->key = Str::random(config('ninja.key_length'));
try { try{
$invitation->save(); $invitation->save();
} catch (\Exception $e) { }
catch(\Exception $e){
$invitation = null; $invitation = null;
} }
} }
} }
private function clientPaidToDateQuery() private function clientPaidToDateQuery()
{ {
$results = \DB::select(\DB::raw(' $results = \DB::select( \DB::raw("
SELECT SELECT
clients.id as client_id, clients.id as client_id,
clients.paid_to_date as client_paid_to_date, clients.paid_to_date as client_paid_to_date,
@ -419,14 +431,14 @@ class CheckData extends Command
GROUP BY clients.id GROUP BY clients.id
HAVING payments_applied != client_paid_to_date HAVING payments_applied != client_paid_to_date
ORDER BY clients.id; ORDER BY clients.id;
')); ") );
return $results; return $results;
} }
private function clientCreditPaymentables($client) private function clientCreditPaymentables($client)
{ {
$results = \DB::select(\DB::raw(' $results = \DB::select( \DB::raw("
SELECT SELECT
SUM(paymentables.amount - paymentables.refunded) as credit_payment SUM(paymentables.amount - paymentables.refunded) as credit_payment
FROM payments FROM payments
@ -438,8 +450,8 @@ class CheckData extends Command
AND paymentables.amount > 0 AND paymentables.amount > 0
AND payments.is_deleted = 0 AND payments.is_deleted = 0
AND payments.client_id = ?; AND payments.client_id = ?;
'), [App\Models\Credit::class, $client->id]); "), [App\Models\Credit::class, $client->id] );
return $results; return $results;
} }
@ -448,8 +460,9 @@ class CheckData extends Command
$clients_to_check = $this->clientPaidToDateQuery(); $clients_to_check = $this->clientPaidToDateQuery();
$this->wrong_paid_to_dates = 0; $this->wrong_paid_to_dates = 0;
foreach ($clients_to_check as $_client) { foreach($clients_to_check as $_client)
{
$client = Client::withTrashed()->find($_client->client_id); $client = Client::withTrashed()->find($_client->client_id);
$credits_from_reversal = Credit::withTrashed()->where('client_id', $client->id)->where('is_deleted', 0)->whereNotNull('invoice_id')->sum('amount'); $credits_from_reversal = Credit::withTrashed()->where('client_id', $client->id)->where('is_deleted', 0)->whereNotNull('invoice_id')->sum('amount');
@ -458,22 +471,26 @@ class CheckData extends Command
$total_paid_to_date = $_client->payments_applied + $credits_used_for_payments[0]->credit_payment - $credits_from_reversal; $total_paid_to_date = $_client->payments_applied + $credits_used_for_payments[0]->credit_payment - $credits_from_reversal;
if (round($total_paid_to_date, 2) != round($_client->client_paid_to_date, 2)) { if(round($total_paid_to_date,2) != round($_client->client_paid_to_date,2)){
$this->wrong_paid_to_dates++; $this->wrong_paid_to_dates++;
$this->logMessage($client->present()->name.' id = # '.$client->id." - Client Paid To Date = {$client->paid_to_date} != Invoice Payments = {$total_paid_to_date} - {$_client->payments_applied} + {$credits_used_for_payments[0]->credit_payment}"); $this->logMessage($client->present()->name.' id = # '.$client->id." - Client Paid To Date = {$client->paid_to_date} != Invoice Payments = {$total_paid_to_date} - {$_client->payments_applied} + {$credits_used_for_payments[0]->credit_payment}");
$this->isValid = false; $this->isValid = false;
if ($this->option('paid_to_date')) { if($this->option('paid_to_date')){
$this->logMessage("# {$client->id} ".$client->present()->name.' - '.$client->number." Fixing {$client->paid_to_date} to {$total_paid_to_date}"); $this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." Fixing {$client->paid_to_date} to {$total_paid_to_date}");
$client->paid_to_date = $total_paid_to_date; $client->paid_to_date = $total_paid_to_date;
$client->save(); $client->save();
} }
} }
} }
$this->logMessage("{$this->wrong_paid_to_dates} clients with incorrect paid to dates"); $this->logMessage("{$this->wrong_paid_to_dates} clients with incorrect paid to dates");
} }
private function checkPaidToDates() private function checkPaidToDates()
@ -482,12 +499,12 @@ class CheckData extends Command
$credit_total_applied = 0; $credit_total_applied = 0;
$clients = DB::table('clients') $clients = DB::table('clients')
->leftJoin('payments', function ($join) { ->leftJoin('payments', function($join) {
$join->on('payments.client_id', '=', 'clients.id') $join->on('payments.client_id', '=', 'clients.id')
->where('payments.is_deleted', 0) ->where('payments.is_deleted', 0)
->whereIn('payments.status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED]); ->whereIn('payments.status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED]);
}) })
->where('clients.is_deleted', 0) ->where('clients.is_deleted',0)
->where('clients.updated_at', '>', now()->subDays(2)) ->where('clients.updated_at', '>', now()->subDays(2))
->groupBy('clients.id') ->groupBy('clients.id')
->havingRaw('clients.paid_to_date != sum(coalesce(payments.amount - payments.refunded, 0))') ->havingRaw('clients.paid_to_date != sum(coalesce(payments.amount - payments.refunded, 0))')
@ -495,16 +512,19 @@ class CheckData extends Command
/* Due to accounting differences we need to perform a second loop here to ensure there actually is an issue */ /* 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) { $clients->each(function ($client_record) use ($credit_total_applied) {
$client = Client::withTrashed()->find($client_record->id); $client = Client::withTrashed()->find($client_record->id);
$total_invoice_payments = 0; $total_invoice_payments = 0;
foreach ($client->invoices()->where('is_deleted', false)->where('status_id', '>', 1)->get() as $invoice) { foreach ($client->invoices()->where('is_deleted', false)->where('status_id', '>', 1)->get() as $invoice) {
$total_invoice_payments += $invoice->payments() $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]) ->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') ->selectRaw('sum(paymentables.amount - paymentables.refunded) as p')
->pluck('p') ->pluck('p')
->first(); ->first();
} }
//commented IN 27/06/2021 - sums ALL client payments AND the unapplied amounts to match the client paid to date //commented IN 27/06/2021 - sums ALL client payments AND the unapplied amounts to match the client paid to date
@ -517,6 +537,7 @@ class CheckData extends Command
// 10/02/21 // 10/02/21
foreach ($client->payments as $payment) { foreach ($client->payments as $payment) {
$credit_total_applied += $payment->paymentables() $credit_total_applied += $payment->paymentables()
->where('paymentable_type', App\Models\Credit::class) ->where('paymentable_type', App\Models\Credit::class)
->selectRaw('sum(paymentables.amount - paymentables.refunded) as p') ->selectRaw('sum(paymentables.amount - paymentables.refunded) as p')
@ -526,7 +547,7 @@ class CheckData extends Command
if ($credit_total_applied < 0) { if ($credit_total_applied < 0) {
$total_invoice_payments += $credit_total_applied; $total_invoice_payments += $credit_total_applied;
} }
if (round($total_invoice_payments, 2) != round($client->paid_to_date, 2)) { if (round($total_invoice_payments, 2) != round($client->paid_to_date, 2)) {
$this->wrong_paid_to_dates++; $this->wrong_paid_to_dates++;
@ -535,8 +556,8 @@ class CheckData extends Command
$this->isValid = false; $this->isValid = false;
if ($this->option('paid_to_date')) { if($this->option('paid_to_date')){
$this->logMessage("# {$client->id} ".$client->present()->name.' - '.$client->number." Fixing {$client->paid_to_date} to {$total_invoice_payments}"); $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->paid_to_date = $total_invoice_payments;
$client->save(); $client->save();
} }
@ -551,7 +572,9 @@ class CheckData extends Command
$this->wrong_balances = 0; $this->wrong_balances = 0;
Client::cursor()->where('is_deleted', 0)->where('clients.updated_at', '>', now()->subDays(2))->each(function ($client) { 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) { $client->invoices->where('is_deleted', false)->whereIn('status_id', '!=', Invoice::STATUS_DRAFT)->each(function ($invoice) use ($client) {
$total_paid = $invoice->payments() $total_paid = $invoice->payments()
->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED]) ->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') ->selectRaw('sum(paymentables.amount - paymentables.refunded) as p')
@ -562,7 +585,7 @@ class CheckData extends Command
$calculated_paid_amount = $invoice->amount - $invoice->balance - $total_credit; $calculated_paid_amount = $invoice->amount - $invoice->balance - $total_credit;
if ((string) $total_paid != (string) ($invoice->amount - $invoice->balance - $total_credit)) { if ((string)$total_paid != (string)($invoice->amount - $invoice->balance - $total_credit)) {
$this->wrong_balances++; $this->wrong_balances++;
$this->logMessage($client->present()->name.' - '.$client->id." - Total Paid = {$total_paid} != Calculated Total = {$calculated_paid_amount}"); $this->logMessage($client->present()->name.' - '.$client->id." - Total Paid = {$total_paid} != Calculated Total = {$calculated_paid_amount}");
@ -570,6 +593,7 @@ class CheckData extends Command
$this->isValid = false; $this->isValid = false;
} }
}); });
}); });
$this->logMessage("{$this->wrong_balances} clients with incorrect invoice balances"); $this->logMessage("{$this->wrong_balances} clients with incorrect invoice balances");
@ -577,7 +601,7 @@ class CheckData extends Command
private function clientBalanceQuery() private function clientBalanceQuery()
{ {
$results = \DB::select(\DB::raw(' $results = \DB::select( \DB::raw("
SELECT SELECT
SUM(invoices.balance) as invoice_balance, SUM(invoices.balance) as invoice_balance,
clients.id as client_id, clients.id as client_id,
@ -591,8 +615,8 @@ class CheckData extends Command
GROUP BY clients.id GROUP BY clients.id
HAVING invoice_balance != clients.balance HAVING invoice_balance != clients.balance
ORDER BY clients.id; ORDER BY clients.id;
')); ") );
return $results; return $results;
} }
@ -603,32 +627,70 @@ class CheckData extends Command
$clients = $this->clientBalanceQuery(); $clients = $this->clientBalanceQuery();
foreach ($clients as $client) { foreach($clients as $client)
$client = (array) $client; {
$client = (array)$client;
if ((string) $client['invoice_balance'] != (string) $client['client_balance']) { if ((string) $client['invoice_balance'] != (string) $client['client_balance']) {
$this->wrong_paid_to_dates++; $this->wrong_paid_to_dates++;
$client_object = Client::withTrashed()->find($client['client_id']); $client_object = Client::withTrashed()->find($client['client_id']);
$this->logMessage($client_object->present()->name.' - '.$client_object->id.' - calculated client balances do not match Invoice Balances = '.$client['invoice_balance'].' - Client Balance = '.rtrim($client['client_balance'], '0')); $this->logMessage($client_object->present()->name.' - '.$client_object->id." - calculated client balances do not match Invoice Balances = ". $client['invoice_balance'] ." - Client Balance = ".rtrim($client['client_balance'], '0'));
if ($this->option('client_balance')) { if($this->option('client_balance')){
$this->logMessage("# {$client_object->id} ".$client_object->present()->name.' - '.$client_object->number." Fixing {$client_object->balance} to ".$client['invoice_balance']);
$this->logMessage("# {$client_object->id} " . $client_object->present()->name.' - '.$client_object->number." Fixing {$client_object->balance} to " . $client['invoice_balance']);
$client_object->balance = $client['invoice_balance']; $client_object->balance = $client['invoice_balance'];
$client_object->save(); $client_object->save();
} }
$this->isValid = false; $this->isValid = false;
} }
} }
$this->logMessage("{$this->wrong_paid_to_dates} clients with incorrect client balances"); $this->logMessage("{$this->wrong_paid_to_dates} clients with incorrect client balances");
} }
private function checkClientBalanceEdgeCases()
{
Client::query()
->where('is_deleted',false)
->where('balance', '!=', 0)
->cursor()
->each(function ($client){
$count = Invoice::withTrashed()
->where('client_id', $client->id)
->where('is_deleted',false)
->whereIn('status_id', [2,3])
->count();
if($count == 0){
$this->logMessage("# {$client->id} # {$client->name} {$client->balance} is invalid should be 0");
if($this->option('client_balance')){
$this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." Fixing {$client->balance} to 0");
$client->balance = 0;
$client->save();
}
}
});
}
private function invoiceBalanceQuery() private function invoiceBalanceQuery()
{ {
$results = \DB::select(\DB::raw(' $results = \DB::select( \DB::raw("
SELECT SELECT
clients.id, clients.id,
clients.balance, clients.balance,
@ -642,8 +704,8 @@ class CheckData extends Command
GROUP BY clients.id GROUP BY clients.id
HAVING(invoices_balance != clients.balance) HAVING(invoices_balance != clients.balance)
ORDER BY clients.id; ORDER BY clients.id;
')); ") );
return $results; return $results;
} }
@ -654,10 +716,11 @@ class CheckData extends Command
$_clients = $this->invoiceBalanceQuery(); $_clients = $this->invoiceBalanceQuery();
foreach ($_clients as $_client) { foreach($_clients as $_client)
{
$client = Client::withTrashed()->find($_client->id); $client = Client::withTrashed()->find($_client->id);
$invoice_balance = $client->invoices()->where('is_deleted', false)->whereIn('status_id', [2, 3])->sum('balance'); $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(); $ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();
@ -665,26 +728,31 @@ class CheckData extends Command
$this->wrong_balances++; $this->wrong_balances++;
$ledger_balance = $ledger ? $ledger->balance : 0; $ledger_balance = $ledger ? $ledger->balance : 0;
$this->logMessage("# {$client->id} ".$client->present()->name.' - '.$client->number." - Balance Failure - Invoice Balances = {$invoice_balance} Client Balance = {$client->balance} Ledger Balance = {$ledger_balance}"); $this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." - Balance Failure - Invoice Balances = {$invoice_balance} Client Balance = {$client->balance} Ledger Balance = {$ledger_balance}");
$this->isValid = false; $this->isValid = false;
if ($this->option('client_balance')) { if($this->option('client_balance')){
$this->logMessage("# {$client->id} ".$client->present()->name.' - '.$client->number." Fixing {$client->balance} to {$invoice_balance}");
$this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." Fixing {$client->balance} to {$invoice_balance}");
$client->balance = $invoice_balance; $client->balance = $invoice_balance;
$client->save(); $client->save();
} }
if ($ledger && (number_format($invoice_balance, 4) != number_format($ledger->balance, 4))) { if($ledger && (number_format($invoice_balance, 4) != number_format($ledger->balance, 4)))
{
$ledger->adjustment = $invoice_balance; $ledger->adjustment = $invoice_balance;
$ledger->balance = $invoice_balance; $ledger->balance = $invoice_balance;
$ledger->notes = 'Ledger Adjustment'; $ledger->notes = 'Ledger Adjustment';
$ledger->save(); $ledger->save();
} }
} }
} }
$this->logMessage("{$this->wrong_balances} clients with incorrect balances"); $this->logMessage("{$this->wrong_balances} clients with incorrect balances");
} }
private function checkLedgerBalances() private function checkLedgerBalances()
@ -693,17 +761,19 @@ class CheckData extends Command
$this->wrong_paid_to_dates = 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::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'); $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(); $ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();
if ($ledger && number_format($ledger->balance, 4) != number_format($client->balance, 4)) { if ($ledger && number_format($ledger->balance, 4) != number_format($client->balance, 4)) {
$this->wrong_balances++; $this->wrong_balances++;
$this->logMessage("# {$client->id} ".$client->present()->name.' - '.$client->number." - Balance Failure - Client Balance = {$client->balance} Ledger Balance = {$ledger->balance}"); $this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." - Balance Failure - Client Balance = {$client->balance} Ledger Balance = {$ledger->balance}");
$this->isValid = false; $this->isValid = false;
if ($this->option('ledger_balance')) {
$this->logMessage("# {$client->id} ".$client->present()->name.' - '.$client->number." Fixing {$client->balance} to {$invoice_balance}"); if($this->option('ledger_balance')){
$this->logMessage("# {$client->id} " . $client->present()->name.' - '.$client->number." Fixing {$client->balance} to {$invoice_balance}");
$client->balance = $invoice_balance; $client->balance = $invoice_balance;
$client->save(); $client->save();
@ -712,6 +782,7 @@ class CheckData extends Command
$ledger->notes = 'Ledger Adjustment'; $ledger->notes = 'Ledger Adjustment';
$ledger->save(); $ledger->save();
} }
} }
} }
@ -803,20 +874,22 @@ class CheckData extends Command
public function checkAccountStatuses() public function checkAccountStatuses()
{ {
Account::where('plan_expires', '<=', now()->subDays(2))->cursor()->each(function ($account) { Account::where('plan_expires', '<=', now()->subDays(2))->cursor()->each(function ($account){
$client = Client::on('db-ninja-01')->where('company_id', config('ninja.ninja_default_company_id'))->where('custom_value2', $account->key)->first();
if ($client) { $client = Client::on('db-ninja-01')->where('company_id', config('ninja.ninja_default_company_id'))->where('custom_value2', $account->key)->first();
if($client){
$payment = Payment::on('db-ninja-01') $payment = Payment::on('db-ninja-01')
->where('company_id', config('ninja.ninja_default_company_id')) ->where('company_id', config('ninja.ninja_default_company_id'))
->where('client_id', $client->id) ->where('client_id', $client->id)
->where('date', '>=', now()->subDays(2)) ->where('date', '>=', now()->subDays(2))
->exists(); ->exists();
if ($payment) { if($payment)
$this->logMessage("I found a payment for {$account->key}"); $this->logMessage("I found a payment for {$account->key}");
}
} }
}); });
} }
@ -824,21 +897,24 @@ class CheckData extends Command
{ {
$this->wrong_paid_status = 0; $this->wrong_paid_status = 0;
foreach (Invoice::with(['payments'])->whereHas('payments')->where('status_id', 4)->where('balance', '>', 0)->where('is_deleted', 0)->cursor() as $invoice) { foreach(Invoice::with(['payments'])->whereHas('payments')->where('status_id', 4)->where('balance', '>', 0)->where('is_deleted',0)->cursor() as $invoice)
{
$this->wrong_paid_status++; $this->wrong_paid_status++;
$this->logMessage("# {$invoice->id} " . ' - '.$invoice->number." - Marked as paid, but balance = {$invoice->balance}");
$this->logMessage("# {$invoice->id} ".' - '.$invoice->number." - Marked as paid, but balance = {$invoice->balance}"); if($this->option('balance_status')){
if ($this->option('balance_status')) {
$val = $invoice->balance; $val = $invoice->balance;
$invoice->balance = 0; $invoice->balance = 0;
$invoice->paid_to_date = $val; $invoice->paid_to_date=$val;
$invoice->save(); $invoice->save();
$p = $invoice->payments->first(); $p = $invoice->payments->first();
if ($p && (int) $p->amount == 0) { if($p && (int)$p->amount == 0)
{
$p->amount = $val; $p->amount = $val;
$p->applied = $val; $p->applied = $val;
$p->save(); $p->save();
@ -848,10 +924,14 @@ class CheckData extends Command
$pivot->save(); $pivot->save();
} }
$this->logMessage("Fixing {$invoice->id} settings payment to {$val}"); $this->logMessage("Fixing {$invoice->id} settings payment to {$val}");
} }
} }
$this->logMessage($this->wrong_paid_status.' wrong invoices with bad balance state'); $this->logMessage($this->wrong_paid_status." wrong invoices with bad balance state");
} }
} }

View File

@ -57,9 +57,9 @@ use stdClass;
class CreateSingleAccount extends Command class CreateSingleAccount extends Command
{ {
use MakesHash, GeneratesCounter; use MakesHash, GeneratesCounter;
protected $description = 'Create Single Sample Account'; protected $description = 'Create Single Sample Account';
protected $signature = 'ninja:create-single-account {gateway=all} {--database=db-ninja-01}'; protected $signature = 'ninja:create-single-account {gateway=all} {--database=db-ninja-01}';
protected $invoice_repo; protected $invoice_repo;
@ -75,13 +75,12 @@ class CreateSingleAccount extends Command
*/ */
public function handle() public function handle()
{ {
if (config('ninja.is_docker')) {
return;
}
if (! $this->confirm('Are you sure you want to inject dummy data?')) { if(config('ninja.is_docker'))
return;
if (!$this->confirm('Are you sure you want to inject dummy data?'))
return; return;
}
$this->invoice_repo = new InvoiceRepository(); $this->invoice_repo = new InvoiceRepository();
@ -106,10 +105,10 @@ class CreateSingleAccount extends Command
$company = Company::factory()->create([ $company = Company::factory()->create([
'account_id' => $account->id, 'account_id' => $account->id,
'slack_webhook_url' => config('ninja.notification.slack'), 'slack_webhook_url' => config('ninja.notification.slack'),
'default_password_timeout' => 30 * 60000, 'default_password_timeout' => 30*60000,
'portal_mode' => 'domain', 'portal_mode' => 'domain',
'portal_domain' => 'http://ninja.test:8000', 'portal_domain' => 'http://ninja.test:8000',
'track_inventory' => true, 'track_inventory' => true
]); ]);
$settings = $company->settings; $settings = $company->settings;
@ -154,31 +153,33 @@ class CreateSingleAccount extends Command
]); ]);
Product::factory()->count(1)->create([ Product::factory()->count(1)->create([
'user_id' => $user->id, 'user_id' => $user->id,
'company_id' => $company->id, 'company_id' => $company->id,
]); ]);
TaxRate::factory()->create([ TaxRate::factory()->create([
'user_id' => $user->id, 'user_id' => $user->id,
'company_id' => $company->id, 'company_id' => $company->id,
'name' => 'GST', 'name' => 'GST',
'rate' => 10, 'rate' => 10
]); ]);
TaxRate::factory()->create([ TaxRate::factory()->create([
'user_id' => $user->id, 'user_id' => $user->id,
'company_id' => $company->id, 'company_id' => $company->id,
'name' => 'VAT', 'name' => 'VAT',
'rate' => 17.5, 'rate' => 17.5
]); ]);
TaxRate::factory()->create([ TaxRate::factory()->create([
'user_id' => $user->id, 'user_id' => $user->id,
'company_id' => $company->id, 'company_id' => $company->id,
'name' => 'CA Sales Tax', 'name' => 'CA Sales Tax',
'rate' => 5, 'rate' => 5
]); ]);
$this->info('Creating '.$this->count.' clients'); $this->info('Creating '.$this->count.' clients');
for ($x = 0; $x < $this->count; $x++) { for ($x = 0; $x < $this->count; $x++) {
@ -188,7 +189,7 @@ class CreateSingleAccount extends Command
$this->createClient($company, $user); $this->createClient($company, $user);
} }
CreateCompanyTaskStatuses::dispatchSync($company, $user); CreateCompanyTaskStatuses::dispatchNow($company, $user);
for ($x = 0; $x < $this->count; $x++) { for ($x = 0; $x < $this->count; $x++) {
$client = $company->clients->random(); $client = $company->clients->random();
@ -226,18 +227,18 @@ class CreateSingleAccount extends Command
$client = $company->clients->random(); $client = $company->clients->random();
$this->info('creating task for client #'.$client->id); $this->info('creating task for client #' . $client->id);
$this->createTask($client); $this->createTask($client);
$client = $company->clients->random(); $client = $company->clients->random();
$this->info('creating project for client #'.$client->id); $this->info('creating project for client #' . $client->id);
$this->createProject($client); $this->createProject($client);
$this->info('creating credit for client #'.$client->id); $this->info('creating credit for client #' . $client->id);
$this->createCredit($client); $this->createCredit($client);
$this->info('creating recurring invoice for client # '.$client->id); $this->info('creating recurring invoice for client # ' . $client->id);
$this->createRecurringInvoice($client); $this->createRecurringInvoice($client);
} }
@ -249,7 +250,7 @@ class CreateSingleAccount extends Command
private function createSubsData($company, $user) private function createSubsData($company, $user)
{ {
$gs = GroupSettingFactory::create($company->id, $user->id); $gs = GroupSettingFactory::create($company->id, $user->id);
$gs->name = 'plans'; $gs->name = "plans";
$gs->save(); $gs->save();
$p1 = Product::factory()->create([ $p1 = Product::factory()->create([
@ -289,7 +290,7 @@ class CreateSingleAccount extends Command
]; ];
$sub = SubscriptionFactory::create($company->id, $user->id); $sub = SubscriptionFactory::create($company->id, $user->id);
$sub->name = 'Pro Plan'; $sub->name = "Pro Plan";
$sub->group_id = $gs->id; $sub->group_id = $gs->id;
$sub->recurring_product_ids = "{$p1->hashed_id}"; $sub->recurring_product_ids = "{$p1->hashed_id}";
$sub->webhook_configuration = $webhook_config; $sub->webhook_configuration = $webhook_config;
@ -298,7 +299,7 @@ class CreateSingleAccount extends Command
$sub->save(); $sub->save();
$sub = SubscriptionFactory::create($company->id, $user->id); $sub = SubscriptionFactory::create($company->id, $user->id);
$sub->name = 'Enterprise Plan'; $sub->name = "Enterprise Plan";
$sub->group_id = $gs->id; $sub->group_id = $gs->id;
$sub->recurring_product_ids = "{$p2->hashed_id}"; $sub->recurring_product_ids = "{$p2->hashed_id}";
$sub->webhook_configuration = $webhook_config; $sub->webhook_configuration = $webhook_config;
@ -307,7 +308,7 @@ class CreateSingleAccount extends Command
$sub->save(); $sub->save();
$sub = SubscriptionFactory::create($company->id, $user->id); $sub = SubscriptionFactory::create($company->id, $user->id);
$sub->name = 'Free Plan'; $sub->name = "Free Plan";
$sub->group_id = $gs->id; $sub->group_id = $gs->id;
$sub->recurring_product_ids = "{$p3->hashed_id}"; $sub->recurring_product_ids = "{$p3->hashed_id}";
$sub->webhook_configuration = $webhook_config; $sub->webhook_configuration = $webhook_config;
@ -323,28 +324,28 @@ class CreateSingleAccount extends Command
// }); // });
$client = Client::factory()->create([ $client = Client::factory()->create([
'user_id' => $user->id, 'user_id' => $user->id,
'company_id' => $company->id, 'company_id' => $company->id,
]); ]);
ClientContact::factory()->create([ ClientContact::factory()->create([
'user_id' => $user->id, 'user_id' => $user->id,
'client_id' => $client->id, 'client_id' => $client->id,
'company_id' => $company->id, 'company_id' => $company->id,
'is_primary' => 1, 'is_primary' => 1,
'email' => 'user@example.com', 'email' => 'user@example.com'
]); ]);
ClientContact::factory()->count(rand(1, 2))->create([ ClientContact::factory()->count(rand(1, 2))->create([
'user_id' => $user->id, 'user_id' => $user->id,
'client_id' => $client->id, 'client_id' => $client->id,
'company_id' => $company->id, 'company_id' => $company->id,
]); ]);
$client->number = $this->getNextClientNumber($client); $client->number = $this->getNextClientNumber($client);
$settings = $client->settings; $settings = $client->settings;
$settings->currency_id = '1'; $settings->currency_id = "1";
// $settings->use_credits_payment = "always"; // $settings->use_credits_payment = "always";
$client->settings = $settings; $client->settings = $settings;
@ -358,48 +359,49 @@ class CreateSingleAccount extends Command
private function createExpense($client) private function createExpense($client)
{ {
Expense::factory()->count(rand(1, 2))->create([ Expense::factory()->count(rand(1, 2))->create([
'user_id' => $client->user->id, 'user_id' => $client->user->id,
'client_id' => $client->id, 'client_id' => $client->id,
'company_id' => $client->company->id, 'company_id' => $client->company->id,
]); ]);
} }
private function createVendor($client) private function createVendor($client)
{ {
$vendor = Vendor::factory()->create([ $vendor = Vendor::factory()->create([
'user_id' => $client->user->id, 'user_id' => $client->user->id,
'company_id' => $client->company->id, 'company_id' => $client->company->id,
]); ]);
VendorContact::factory()->create([ VendorContact::factory()->create([
'user_id' => $client->user->id, 'user_id' => $client->user->id,
'vendor_id' => $vendor->id, 'vendor_id' => $vendor->id,
'company_id' => $client->company->id, 'company_id' => $client->company->id,
'is_primary' => 1, 'is_primary' => 1,
]); ]);
VendorContact::factory()->count(rand(1, 2))->create([ VendorContact::factory()->count(rand(1, 2))->create([
'user_id' => $client->user->id, 'user_id' => $client->user->id,
'vendor_id' => $vendor->id, 'vendor_id' => $vendor->id,
'company_id' => $client->company->id, 'company_id' => $client->company->id,
'is_primary' => 0, 'is_primary' => 0,
]); ]);
} }
private function createTask($client) private function createTask($client)
{ {
$vendor = Task::factory()->create([ $vendor = Task::factory()->create([
'user_id' => $client->user->id, 'user_id' => $client->user->id,
'company_id' => $client->company->id, 'company_id' => $client->company->id,
]); ]);
} }
private function createProject($client) private function createProject($client)
{ {
$vendor = Project::factory()->create([ $vendor = Project::factory()->create([
'user_id' => $client->user->id, 'user_id' => $client->user->id,
'company_id' => $client->company->id, 'company_id' => $client->company->id,
]); 'client_id' => $client->id,
]);
} }
private function createInvoice($client) private function createInvoice($client)
@ -429,7 +431,7 @@ class CreateSingleAccount extends Command
$invoice->tax_rate3 = 5; $invoice->tax_rate3 = 5;
} }
$invoice->custom_value1 = $faker->date(); $invoice->custom_value1 = $faker->date;
$invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no'; $invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no';
$invoice->save(); $invoice->save();
@ -516,6 +518,7 @@ class CreateSingleAccount extends Command
$quote->service()->createInvitations(); $quote->service()->createInvitations();
} }
private function buildCreditItem() private function buildCreditItem()
{ {
$line_items = []; $line_items = [];
@ -536,9 +539,11 @@ class CreateSingleAccount extends Command
$line_items[] = $item; $line_items[] = $item;
return $line_items; return $line_items;
} }
private function buildLineItems($count = 1) private function buildLineItems($count = 1)
{ {
$line_items = []; $line_items = [];
@ -610,6 +615,7 @@ class CreateSingleAccount extends Command
private function createGateways($company, $user) private function createGateways($company, $user)
{ {
if (config('ninja.testvars.stripe') && ($this->gateway == 'all' || $this->gateway == 'stripe')) { if (config('ninja.testvars.stripe') && ($this->gateway == 'all' || $this->gateway == 'stripe')) {
$cg = new CompanyGateway; $cg = new CompanyGateway;
$cg->company_id = $company->id; $cg->company_id = $company->id;
$cg->user_id = $user->id; $cg->user_id = $user->id;
@ -628,6 +634,8 @@ class CreateSingleAccount extends Command
$cg->fees_and_limits = $fees_and_limits; $cg->fees_and_limits = $fees_and_limits;
$cg->save(); $cg->save();
} }
if (config('ninja.testvars.paypal') && ($this->gateway == 'all' || $this->gateway == 'paypal')) { if (config('ninja.testvars.paypal') && ($this->gateway == 'all' || $this->gateway == 'paypal')) {
@ -735,6 +743,7 @@ class CreateSingleAccount extends Command
$cg->save(); $cg->save();
} }
if (config('ninja.testvars.paytrace.decrypted') && ($this->gateway == 'all' || $this->gateway == 'paytrace')) { if (config('ninja.testvars.paytrace.decrypted') && ($this->gateway == 'all' || $this->gateway == 'paytrace')) {
$cg = new CompanyGateway; $cg = new CompanyGateway;
$cg->company_id = $company->id; $cg->company_id = $company->id;
@ -748,6 +757,7 @@ class CreateSingleAccount extends Command
$cg->save(); $cg->save();
$gateway_types = $cg->driver()->gatewayTypes(); $gateway_types = $cg->driver()->gatewayTypes();
$fees_and_limits = new stdClass; $fees_and_limits = new stdClass;
@ -827,7 +837,7 @@ class CreateSingleAccount extends Command
$invoice->tax_rate3 = 5; $invoice->tax_rate3 = 5;
} }
$invoice->custom_value1 = $faker->date(); $invoice->custom_value1 = $faker->date;
$invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no'; $invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no';
$invoice->status_id = RecurringInvoice::STATUS_ACTIVE; $invoice->status_id = RecurringInvoice::STATUS_ACTIVE;

View File

@ -97,8 +97,20 @@ class PaymentController extends Controller
$client = $invoice ? $invoice->client : auth()->guard('contact')->user()->client; $client = $invoice ? $invoice->client : auth()->guard('contact')->user()->client;
// 09-07-2022 catch duplicate responses for invoices that already paid here. // 09-07-2022 catch duplicate responses for invoices that already paid here.
if($invoice && $invoice->status_id == Invoice::STATUS_PAID) if($invoice && $invoice->status_id == Invoice::STATUS_PAID){
abort(400, 'Invoice paid. Duplicate submission');
$data = [
'invoice' => $invoice,
'key' => false
];
if ($request->query('mode') === 'fullscreen') {
return render('invoices.show-fullscreen', $data);
}
return $this->render('invoices.show', $data);
}
return $gateway return $gateway
->driver($client) ->driver($client)

View File

@ -63,9 +63,6 @@ class UpdateInvoiceRequest extends Request
$rules['discount'] = 'sometimes|numeric'; $rules['discount'] = 'sometimes|numeric';
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())]; $rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
// if($this->input('status_id') != Invoice::STATUS_DRAFT)
// $rules['balance'] = new InvoiceBalanceSanity($this->invoice, $this->all());
return $rules; return $rules;
} }

View File

@ -129,7 +129,7 @@ class Request extends FormRequest
} }
} }
if (isset($input['invitations'])) { if (isset($input['invitations']) && is_array($input['invitations'])) {
foreach ($input['invitations'] as $key => $value) { foreach ($input['invitations'] as $key => $value) {
if (isset($input['invitations'][$key]['id']) && is_numeric($input['invitations'][$key]['id'])) { if (isset($input['invitations'][$key]['id']) && is_numeric($input['invitations'][$key]['id'])) {
unset($input['invitations'][$key]['id']); unset($input['invitations'][$key]['id']);

View File

@ -20,6 +20,7 @@ use App\Mail\DownloadInvoices;
use App\Models\Company; use App\Models\Company;
use App\Models\CreditInvitation; use App\Models\CreditInvitation;
use App\Models\InvoiceInvitation; use App\Models\InvoiceInvitation;
use App\Models\PurchaseOrderInvitation;
use App\Models\QuoteInvitation; use App\Models\QuoteInvitation;
use App\Models\RecurringInvoice; use App\Models\RecurringInvoice;
use App\Models\RecurringInvoiceInvitation; use App\Models\RecurringInvoiceInvitation;
@ -68,6 +69,7 @@ class CompanyExport implements ShouldQueue
*/ */
public function handle() public function handle()
{ {
MultiDB::setDb($this->company->db); MultiDB::setDb($this->company->db);
$this->company = Company::where('company_key', $this->company->company_key)->first(); $this->company = Company::where('company_key', $this->company->company_key)->first();
@ -76,7 +78,8 @@ class CompanyExport implements ShouldQueue
$this->export_data['app_version'] = config('ninja.app_version'); $this->export_data['app_version'] = config('ninja.app_version');
$this->export_data['activities'] = $this->company->all_activities->map(function ($activity) { $this->export_data['activities'] = $this->company->all_activities->map(function ($activity){
$activity = $this->transformArrayOfKeys($activity, [ $activity = $this->transformArrayOfKeys($activity, [
'user_id', 'user_id',
'company_id', 'company_id',
@ -94,10 +97,11 @@ class CompanyExport implements ShouldQueue
'token_id', 'token_id',
'quote_id', 'quote_id',
'subscription_id', 'subscription_id',
'recurring_invoice_id', 'recurring_invoice_id'
]); ]);
return $activity; return $activity;
})->makeHidden(['id'])->all(); })->makeHidden(['id'])->all();
// $this->export_data['backups'] = $this->company->all_activities()->with('backup')->cursor()->map(function ($activity){ // $this->export_data['backups'] = $this->company->all_activities()->with('backup')->cursor()->map(function ($activity){
@ -113,276 +117,387 @@ class CompanyExport implements ShouldQueue
// })->all(); // })->all();
$this->export_data['users'] = $this->company->users()->withTrashed()->cursor()->map(function ($user) { $this->export_data['users'] = $this->company->users()->withTrashed()->cursor()->map(function ($user){
$user->account_id = $this->encodePrimaryKey($user->account_id); $user->account_id = $this->encodePrimaryKey($user->account_id);
// $user->id = $this->encodePrimaryKey($user->id); // $user->id = $this->encodePrimaryKey($user->id);
return $user->makeVisible(['id']); return $user->makeVisible(['id']);
})->all(); })->all();
$this->export_data['client_contacts'] = $this->company->client_contacts->map(function ($client_contact) {
$this->export_data['client_contacts'] = $this->company->client_contacts->map(function ($client_contact){
$client_contact = $this->transformArrayOfKeys($client_contact, ['company_id', 'user_id', 'client_id']); $client_contact = $this->transformArrayOfKeys($client_contact, ['company_id', 'user_id', 'client_id']);
return $client_contact->makeVisible([ return $client_contact->makeVisible([
'password', 'password',
'remember_token', 'remember_token',
'user_id', 'user_id',
'company_id', 'company_id',
'client_id', 'client_id',
'google_2fa_secret', 'google_2fa_secret',
'id', 'id',
'oauth_provider_id', 'oauth_provider_id',
'oauth_user_id', 'oauth_user_id',
'token', 'token',
'hashed_id', 'hashed_id',
]); ]);
})->all(); })->all();
$this->export_data['client_gateway_tokens'] = $this->company->client_gateway_tokens->map(function ($client_gateway_token) {
$this->export_data['client_gateway_tokens'] = $this->company->client_gateway_tokens->map(function ($client_gateway_token){
$client_gateway_token = $this->transformArrayOfKeys($client_gateway_token, ['company_id', 'client_id', 'company_gateway_id']); $client_gateway_token = $this->transformArrayOfKeys($client_gateway_token, ['company_id', 'client_id', 'company_gateway_id']);
return $client_gateway_token->makeVisible(['id']); return $client_gateway_token->makeVisible(['id']);
})->all(); })->all();
$this->export_data['clients'] = $this->company->clients()->orderBy('number', 'DESC')->cursor()->map(function ($client) {
$this->export_data['clients'] = $this->company->clients()->orderBy('number', 'DESC')->cursor()->map(function ($client){
$client = $this->transformArrayOfKeys($client, ['company_id', 'user_id', 'assigned_user_id', 'group_settings_id']); $client = $this->transformArrayOfKeys($client, ['company_id', 'user_id', 'assigned_user_id', 'group_settings_id']);
return $client->makeVisible(['id', 'private_notes', 'user_id', 'company_id', 'last_login', 'hashed_id']); return $client->makeVisible(['id','private_notes','user_id','company_id','last_login','hashed_id']);
})->all(); })->all();
$this->export_data['company'] = $this->company->toArray(); $this->export_data['company'] = $this->company->toArray();
$this->export_data['company_gateways'] = $this->company->company_gateways()->withTrashed()->cursor()->map(function ($company_gateway) { $this->export_data['company_gateways'] = $this->company->company_gateways()->withTrashed()->cursor()->map(function ($company_gateway){
$company_gateway = $this->transformArrayOfKeys($company_gateway, ['company_id', 'user_id']); $company_gateway = $this->transformArrayOfKeys($company_gateway, ['company_id', 'user_id']);
$company_gateway->config = decrypt($company_gateway->config); $company_gateway->config = decrypt($company_gateway->config);
return $company_gateway->makeVisible(['id']); return $company_gateway->makeVisible(['id']);
})->all(); })->all();
$this->export_data['company_tokens'] = $this->company->tokens->map(function ($token) { $this->export_data['company_tokens'] = $this->company->tokens->map(function ($token){
$token = $this->transformArrayOfKeys($token, ['company_id', 'account_id', 'user_id']); $token = $this->transformArrayOfKeys($token, ['company_id', 'account_id', 'user_id']);
return $token; return $token;
})->all(); })->all();
$this->export_data['company_ledger'] = $this->company->ledger->map(function ($ledger) { $this->export_data['company_ledger'] = $this->company->ledger->map(function ($ledger){
$ledger = $this->transformArrayOfKeys($ledger, ['activity_id', 'client_id', 'company_id', 'account_id', 'user_id', 'company_ledgerable_id']);
$ledger = $this->transformArrayOfKeys($ledger, ['activity_id', 'client_id', 'company_id', 'account_id', 'user_id','company_ledgerable_id']);
return $ledger; return $ledger;
})->all(); })->all();
$this->export_data['company_users'] = $this->company->company_users()->without(['user', 'account'])->cursor()->map(function ($company_user) { $this->export_data['company_users'] = $this->company->company_users()->without(['user','account'])->cursor()->map(function ($company_user){
$company_user = $this->transformArrayOfKeys($company_user, ['company_id', 'account_id', 'user_id']); $company_user = $this->transformArrayOfKeys($company_user, ['company_id', 'account_id', 'user_id']);
return $company_user; return $company_user;
})->all(); })->all();
$this->export_data['credits'] = $this->company->credits()->orderBy('number', 'DESC')->cursor()->map(function ($credit) { $this->export_data['credits'] = $this->company->credits()->orderBy('number', 'DESC')->cursor()->map(function ($credit){
$credit = $this->transformBasicEntities($credit); $credit = $this->transformBasicEntities($credit);
$credit = $this->transformArrayOfKeys($credit, ['recurring_id', 'client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id', 'invoice_id']); $credit = $this->transformArrayOfKeys($credit, ['recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id','invoice_id']);
return $credit->makeVisible(['id']); return $credit->makeVisible(['id']);
})->all(); })->all();
$this->export_data['credit_invitations'] = CreditInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($credit) {
$this->export_data['credit_invitations'] = CreditInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($credit){
$credit = $this->transformArrayOfKeys($credit, ['company_id', 'user_id', 'client_contact_id', 'credit_id']); $credit = $this->transformArrayOfKeys($credit, ['company_id', 'user_id', 'client_contact_id', 'credit_id']);
return $credit->makeVisible(['id']); return $credit->makeVisible(['id']);
})->all(); })->all();
$this->export_data['designs'] = $this->company->user_designs->makeHidden(['id'])->all(); $this->export_data['designs'] = $this->company->user_designs->makeHidden(['id'])->all();
$this->export_data['documents'] = $this->company->all_documents->map(function ($document) { $this->export_data['documents'] = $this->company->all_documents->map(function ($document){
$document = $this->transformArrayOfKeys($document, ['user_id', 'assigned_user_id', 'company_id', 'project_id', 'vendor_id', 'documentable_id']);
$document = $this->transformArrayOfKeys($document, ['user_id', 'assigned_user_id', 'company_id', 'project_id', 'vendor_id','documentable_id']);
$document->hashed_id = $this->encodePrimaryKey($document->id); $document->hashed_id = $this->encodePrimaryKey($document->id);
return $document->makeVisible(['id']); return $document->makeVisible(['id']);
})->all(); })->all();
$this->export_data['expense_categories'] = $this->company->expense_categories->map(function ($expense_category) { $this->export_data['expense_categories'] = $this->company->expense_categories->map(function ($expense_category){
$expense_category = $this->transformArrayOfKeys($expense_category, ['user_id', 'company_id']); $expense_category = $this->transformArrayOfKeys($expense_category, ['user_id', 'company_id']);
return $expense_category->makeVisible(['id']); return $expense_category->makeVisible(['id']);
})->all(); })->all();
$this->export_data['expenses'] = $this->company->expenses()->orderBy('number', 'DESC')->cursor()->map(function ($expense) {
$this->export_data['expenses'] = $this->company->expenses()->orderBy('number', 'DESC')->cursor()->map(function ($expense){
$expense = $this->transformBasicEntities($expense); $expense = $this->transformBasicEntities($expense);
$expense = $this->transformArrayOfKeys($expense, ['vendor_id', 'invoice_id', 'client_id', 'category_id', 'recurring_expense_id', 'project_id']); $expense = $this->transformArrayOfKeys($expense, ['vendor_id', 'invoice_id', 'client_id', 'category_id', 'recurring_expense_id','project_id']);
return $expense->makeVisible(['id']); return $expense->makeVisible(['id']);
})->all(); })->all();
$this->export_data['group_settings'] = $this->company->group_settings->map(function ($gs) { $this->export_data['group_settings'] = $this->company->group_settings->map(function ($gs){
$gs = $this->transformArrayOfKeys($gs, ['user_id', 'company_id']); $gs = $this->transformArrayOfKeys($gs, ['user_id', 'company_id']);
return $gs->makeVisible(['id']); return $gs->makeVisible(['id']);
})->all(); })->all();
$this->export_data['invoices'] = $this->company->invoices()->orderBy('number', 'DESC')->cursor()->map(function ($invoice) {
$this->export_data['invoices'] = $this->company->invoices()->orderBy('number', 'DESC')->cursor()->map(function ($invoice){
$invoice = $this->transformBasicEntities($invoice); $invoice = $this->transformBasicEntities($invoice);
$invoice = $this->transformArrayOfKeys($invoice, ['recurring_id', 'client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id', 'project_id']); $invoice = $this->transformArrayOfKeys($invoice, ['recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id','project_id']);
return $invoice->makeVisible(['id', return $invoice->makeVisible(['id',
'private_notes', 'private_notes',
'user_id', 'user_id',
'client_id', 'client_id',
'company_id', ]); 'company_id',]);
})->all(); })->all();
$this->export_data['invoice_invitations'] = InvoiceInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($invoice) {
$this->export_data['invoice_invitations'] = InvoiceInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($invoice){
$invoice = $this->transformArrayOfKeys($invoice, ['company_id', 'user_id', 'client_contact_id', 'invoice_id']); $invoice = $this->transformArrayOfKeys($invoice, ['company_id', 'user_id', 'client_contact_id', 'invoice_id']);
return $invoice->makeVisible(['id']); return $invoice->makeVisible(['id']);
})->all(); })->all();
$this->export_data['payment_terms'] = $this->company->user_payment_terms->map(function ($term) { $this->export_data['payment_terms'] = $this->company->user_payment_terms->map(function ($term){
$term = $this->transformArrayOfKeys($term, ['user_id', 'company_id']); $term = $this->transformArrayOfKeys($term, ['user_id', 'company_id']);
return $term; return $term;
})->makeHidden(['id'])->all(); })->makeHidden(['id'])->all();
$this->export_data['payments'] = $this->company->payments()->orderBy('number', 'DESC')->cursor()->map(function ($payment) {
$this->export_data['payments'] = $this->company->payments()->orderBy('number', 'DESC')->cursor()->map(function ($payment){
$payment = $this->transformBasicEntities($payment); $payment = $this->transformBasicEntities($payment);
$payment = $this->transformArrayOfKeys($payment, ['client_id', 'project_id', 'vendor_id', 'client_contact_id', 'invitation_id', 'company_gateway_id']); $payment = $this->transformArrayOfKeys($payment, ['client_id','project_id', 'vendor_id', 'client_contact_id', 'invitation_id', 'company_gateway_id']);
$payment->paymentables = $this->transformPaymentable($payment); $payment->paymentables = $this->transformPaymentable($payment);
return $payment->makeVisible(['id']); return $payment->makeVisible(['id']);
})->all(); })->all();
$this->export_data['products'] = $this->company->products->map(function ($product) { $this->export_data['products'] = $this->company->products->map(function ($product){
$product = $this->transformBasicEntities($product); $product = $this->transformBasicEntities($product);
$product = $this->transformArrayOfKeys($product, ['vendor_id', 'project_id']); $product = $this->transformArrayOfKeys($product, ['vendor_id','project_id']);
return $product->makeVisible(['id']); return $product->makeVisible(['id']);
})->all(); })->all();
$this->export_data['projects'] = $this->company->projects()->orderBy('number', 'DESC')->cursor()->map(function ($project) { $this->export_data['projects'] = $this->company->projects()->orderBy('number', 'DESC')->cursor()->map(function ($project){
$project = $this->transformBasicEntities($project); $project = $this->transformBasicEntities($project);
$project = $this->transformArrayOfKeys($project, ['client_id']); $project = $this->transformArrayOfKeys($project, ['client_id']);
return $project->makeVisible(['id']); return $project->makeVisible(['id']);
})->all(); })->all();
$this->export_data['quotes'] = $this->company->quotes()->orderBy('number', 'DESC')->cursor()->map(function ($quote) { $this->export_data['quotes'] = $this->company->quotes()->orderBy('number', 'DESC')->cursor()->map(function ($quote){
$quote = $this->transformBasicEntities($quote); $quote = $this->transformBasicEntities($quote);
$quote = $this->transformArrayOfKeys($quote, ['invoice_id', 'recurring_id', 'client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']); $quote = $this->transformArrayOfKeys($quote, ['invoice_id','recurring_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
return $quote->makeVisible(['id']); return $quote->makeVisible(['id']);
})->all(); })->all();
$this->export_data['quote_invitations'] = QuoteInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($quote) {
$this->export_data['quote_invitations'] = QuoteInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($quote){
$quote = $this->transformArrayOfKeys($quote, ['company_id', 'user_id', 'client_contact_id', 'quote_id']); $quote = $this->transformArrayOfKeys($quote, ['company_id', 'user_id', 'client_contact_id', 'quote_id']);
return $quote->makeVisible(['id']); return $quote->makeVisible(['id']);
})->all(); })->all();
$this->export_data['recurring_expenses'] = $this->company->recurring_expenses()->orderBy('number', 'DESC')->cursor()->map(function ($expense) { $this->export_data['recurring_expenses'] = $this->company->recurring_expenses()->orderBy('number', 'DESC')->cursor()->map(function ($expense){
$expense = $this->transformBasicEntities($expense); $expense = $this->transformBasicEntities($expense);
$expense = $this->transformArrayOfKeys($expense, ['vendor_id', 'invoice_id', 'client_id', 'category_id', 'project_id']); $expense = $this->transformArrayOfKeys($expense, ['vendor_id', 'invoice_id', 'client_id', 'category_id', 'project_id']);
return $expense->makeVisible(['id']); return $expense->makeVisible(['id']);
})->all(); })->all();
$this->export_data['recurring_invoices'] = $this->company->recurring_invoices()->orderBy('number', 'DESC')->cursor()->map(function ($ri) { $this->export_data['recurring_invoices'] = $this->company->recurring_invoices()->orderBy('number', 'DESC')->cursor()->map(function ($ri){
$ri = $this->transformBasicEntities($ri); $ri = $this->transformBasicEntities($ri);
$ri = $this->transformArrayOfKeys($ri, ['client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']); $ri = $this->transformArrayOfKeys($ri, ['client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id']);
return $ri->makeVisible(['id']); return $ri->makeVisible(['id']);
})->all(); })->all();
$this->export_data['recurring_invoice_invitations'] = RecurringInvoiceInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($ri) {
$this->export_data['recurring_invoice_invitations'] = RecurringInvoiceInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($ri){
$ri = $this->transformArrayOfKeys($ri, ['company_id', 'user_id', 'client_contact_id', 'recurring_invoice_id']); $ri = $this->transformArrayOfKeys($ri, ['company_id', 'user_id', 'client_contact_id', 'recurring_invoice_id']);
return $ri; return $ri;
})->all(); })->all();
$this->export_data['subscriptions'] = $this->company->subscriptions->map(function ($subscription) { $this->export_data['subscriptions'] = $this->company->subscriptions->map(function ($subscription){
$subscription = $this->transformBasicEntities($subscription); $subscription = $this->transformBasicEntities($subscription);
$subscription->group_id = $this->encodePrimaryKey($subscription->group_id); $subscription->group_id = $this->encodePrimaryKey($subscription->group_id);
return $subscription->makeVisible(['id', return $subscription->makeVisible([ 'id',
'user_id', 'user_id',
'assigned_user_id', 'assigned_user_id',
'company_id', 'company_id',
'product_ids', 'product_ids',
'recurring_product_ids', 'recurring_product_ids',
'group_id', ]); 'group_id']);
})->all(); })->all();
$this->export_data['system_logs'] = $this->company->system_logs->map(function ($log) {
$this->export_data['system_logs'] = $this->company->system_logs->map(function ($log){
$log->client_id = $this->encodePrimaryKey($log->client_id); $log->client_id = $this->encodePrimaryKey($log->client_id);
$log->company_id = $this->encodePrimaryKey($log->company_id); $log->company_id = $this->encodePrimaryKey($log->company_id);
return $log; return $log;
})->makeHidden(['id'])->all(); })->makeHidden(['id'])->all();
$this->export_data['tasks'] = $this->company->tasks()->orderBy('number', 'DESC')->cursor()->map(function ($task) { $this->export_data['tasks'] = $this->company->tasks()->orderBy('number', 'DESC')->cursor()->map(function ($task){
$task = $this->transformBasicEntities($task); $task = $this->transformBasicEntities($task);
$task = $this->transformArrayOfKeys($task, ['client_id', 'invoice_id', 'project_id', 'status_id']); $task = $this->transformArrayOfKeys($task, ['client_id', 'invoice_id', 'project_id', 'status_id']);
return $task->makeVisible(['id']); return $task->makeVisible(['id']);
})->all(); })->all();
$this->export_data['task_statuses'] = $this->company->task_statuses->map(function ($status) { $this->export_data['task_statuses'] = $this->company->task_statuses->map(function ($status){
$status->id = $this->encodePrimaryKey($status->id); $status->id = $this->encodePrimaryKey($status->id);
$status->user_id = $this->encodePrimaryKey($status->user_id); $status->user_id = $this->encodePrimaryKey($status->user_id);
$status->company_id = $this->encodePrimaryKey($status->company_id); $status->company_id = $this->encodePrimaryKey($status->company_id);
return $status; return $status;
})->all(); })->all();
$this->export_data['tax_rates'] = $this->company->tax_rates->map(function ($rate) { $this->export_data['tax_rates'] = $this->company->tax_rates->map(function ($rate){
$rate->company_id = $this->encodePrimaryKey($rate->company_id); $rate->company_id = $this->encodePrimaryKey($rate->company_id);
$rate->user_id = $this->encodePrimaryKey($rate->user_id); $rate->user_id = $this->encodePrimaryKey($rate->user_id);
return $rate; return $rate;
})->makeHidden(['id'])->all(); })->makeHidden(['id'])->all();
$this->export_data['vendors'] = $this->company->vendors()->orderBy('number', 'DESC')->cursor()->map(function ($vendor) { $this->export_data['vendors'] = $this->company->vendors()->orderBy('number', 'DESC')->cursor()->map(function ($vendor){
return $this->transformBasicEntities($vendor)->makeVisible(['id']); return $this->transformBasicEntities($vendor)->makeVisible(['id']);
})->all(); })->all();
$this->export_data['vendor_contacts'] = VendorContact::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($vendor) {
$this->export_data['vendor_contacts'] = VendorContact::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($vendor){
$vendor = $this->transformBasicEntities($vendor); $vendor = $this->transformBasicEntities($vendor);
$vendor->vendor_id = $this->encodePrimaryKey($vendor->vendor_id); $vendor = $this->transformArrayOfKeys($vendor, ['vendor_id']);
return $vendor->makeVisible(['id','user_id']);
return $vendor->makeVisible(['id']);
})->all(); })->all();
$this->export_data['webhooks'] = $this->company->webhooks->map(function ($hook) { $this->export_data['webhooks'] = $this->company->webhooks->map(function ($hook){
$hook->user_id = $this->encodePrimaryKey($hook->user_id); $hook->user_id = $this->encodePrimaryKey($hook->user_id);
$hook->company_id = $this->encodePrimaryKey($hook->company_id); $hook->company_id = $this->encodePrimaryKey($hook->company_id);
return $hook; return $hook;
})->makeHidden(['id'])->all(); })->makeHidden(['id'])->all();
$this->export_data['purchase_orders'] = $this->company->purchase_orders()->orderBy('number', 'DESC')->cursor()->map(function ($purchase_order){
$purchase_order = $this->transformBasicEntities($purchase_order);
$purchase_order = $this->transformArrayOfKeys($purchase_order, ['expense_id','client_id', 'vendor_id', 'project_id', 'design_id', 'subscription_id','project_id']);
return $purchase_order->makeVisible(['id',
'private_notes',
'user_id',
'client_id',
'vendor_id',
'company_id',]);
})->all();
$this->export_data['purchase_order_invitations'] = PurchaseOrderInvitation::where('company_id', $this->company->id)->withTrashed()->cursor()->map(function ($purchase_order){
$purchase_order = $this->transformArrayOfKeys($purchase_order, ['company_id', 'user_id', 'vendor_contact_id', 'purchase_order_id']);
return $purchase_order->makeVisible(['id']);
})->all();
//write to tmp and email to owner(); //write to tmp and email to owner();
$this->zipAndSend();
$this->zipAndSend(); return true;
return true;
} }
private function transformBasicEntities($model) private function transformBasicEntities($model)
{ {
return $this->transformArrayOfKeys($model, ['user_id', 'assigned_user_id', 'company_id']); return $this->transformArrayOfKeys($model, ['user_id', 'assigned_user_id', 'company_id']);
} }
private function transformArrayOfKeys($model, $keys) private function transformArrayOfKeys($model, $keys)
{ {
foreach ($keys as $key) {
foreach($keys as $key){
$model->{$key} = $this->encodePrimaryKey($model->{$key}); $model->{$key} = $this->encodePrimaryKey($model->{$key});
} }
return $model; return $model;
} }
private function transformPaymentable($payment) private function transformPaymentable($payment)
{ {
$new_arr = []; $new_arr = [];
foreach ($payment->paymentables as $paymentable) { foreach($payment->paymentables as $paymentable)
{
$paymentable->payment_id = $this->encodePrimaryKey($paymentable->payment_id); $paymentable->payment_id = $this->encodePrimaryKey($paymentable->payment_id);
$paymentable->paymentable_id = $this->encodePrimaryKey($paymentable->paymentable_id); $paymentable->paymentable_id = $this->encodePrimaryKey($paymentable->paymentable_id);
@ -390,29 +505,30 @@ class CompanyExport implements ShouldQueue
} }
return $new_arr; return $new_arr;
} }
private function zipAndSend() private function zipAndSend()
{ {
$file_name = date('Y-m-d').'_'.str_replace([' ', '/'], ['_', ''], $this->company->present()->name().'_'.$this->company->company_key.'.zip');
$file_name = date('Y-m-d').'_'.str_replace([" ", "/"],["_",""], $this->company->present()->name() . '_' . $this->company->company_key .'.zip');
$path = 'backups'; $path = 'backups';
if (! Storage::disk(config('filesystems.default'))->exists($path)) { if(!Storage::disk(config('filesystems.default'))->exists($path))
Storage::disk(config('filesystems.default'))->makeDirectory($path, 0775); Storage::disk(config('filesystems.default'))->makeDirectory($path, 0775);
}
$zip_path = public_path('storage/backups/'.$file_name); $zip_path = public_path('storage/backups/'.$file_name);
$zip = new \ZipArchive(); $zip = new \ZipArchive();
if ($zip->open($zip_path, \ZipArchive::CREATE) !== true) { if ($zip->open($zip_path, \ZipArchive::CREATE)!==TRUE) {
nlog("cannot open {$zip_path}"); nlog("cannot open {$zip_path}");
} }
$zip->addFromString('backup.json', json_encode($this->export_data)); $zip->addFromString("backup.json", json_encode($this->export_data));
$zip->close(); $zip->close();
if (Ninja::isHosted()) { if(Ninja::isHosted()) {
Storage::disk(config('filesystems.default'))->put('backups/'.$file_name, file_get_contents($zip_path)); Storage::disk(config('filesystems.default'))->put('backups/'.$file_name, file_get_contents($zip_path));
} }
@ -422,19 +538,20 @@ class CompanyExport implements ShouldQueue
$t = app('translator'); $t = app('translator');
$t->replace(Ninja::transformTranslations($this->company->settings)); $t->replace(Ninja::transformTranslations($this->company->settings));
$company_reference = Company::find($this->company->id); $company_reference = Company::find($this->company->id);;
$nmo = new NinjaMailerObject; $nmo = new NinjaMailerObject;
$nmo->mailable = new DownloadBackup($storage_file_path, $company_reference); $nmo->mailable = new DownloadBackup($storage_file_path, $company_reference);
$nmo->to_user = $this->user; $nmo->to_user = $this->user;
$nmo->company = $company_reference; $nmo->company = $company_reference;
$nmo->settings = $this->company->settings; $nmo->settings = $this->company->settings;
NinjaMailerJob::dispatch($nmo, true); NinjaMailerJob::dispatch($nmo, true);
if (Ninja::isHosted()) { if(Ninja::isHosted()){
sleep(3); sleep(3);
unlink($zip_path); unlink($zip_path);
} }
} }
} }

View File

@ -45,6 +45,8 @@ use App\Models\PaymentTerm;
use App\Models\Paymentable; use App\Models\Paymentable;
use App\Models\Product; use App\Models\Product;
use App\Models\Project; use App\Models\Project;
use App\Models\PurchaseOrder;
use App\Models\PurchaseOrderInvitation;
use App\Models\Quote; use App\Models\Quote;
use App\Models\QuoteInvitation; use App\Models\QuoteInvitation;
use App\Models\RecurringExpense; use App\Models\RecurringExpense;
@ -74,7 +76,6 @@ use Illuminate\Support\Str;
use JsonMachine\JsonDecoder\ExtJsonDecoder; use JsonMachine\JsonDecoder\ExtJsonDecoder;
use JsonMachine\JsonMachine; use JsonMachine\JsonMachine;
use ZipArchive; use ZipArchive;
use function GuzzleHttp\json_encode; use function GuzzleHttp\json_encode;
class CompanyImport implements ShouldQueue class CompanyImport implements ShouldQueue
@ -122,6 +123,7 @@ class CompanyImport implements ShouldQueue
'clients', 'clients',
'client_contacts', 'client_contacts',
'vendors', 'vendors',
'vendor_contacts',
'projects', 'projects',
'products', 'products',
'company_gateways', 'company_gateways',
@ -147,6 +149,8 @@ class CompanyImport implements ShouldQueue
'documents', 'documents',
'webhooks', 'webhooks',
'system_logs', 'system_logs',
'purchase_orders',
'purchase_order_invitations'
]; ];
private $company_properties = [ private $company_properties = [
@ -454,7 +458,7 @@ class CompanyImport implements ShouldQueue
$settings->ticket_number_counter = 1; $settings->ticket_number_counter = 1;
$settings->payment_number_counter = 1; $settings->payment_number_counter = 1;
$settings->project_number_counter = 1; $settings->project_number_counter = 1;
$settings->purchase_order_counter = 1;
$this->company->settings = $co->settings; $this->company->settings = $co->settings;
// $this->company->settings = $this->backup_file->company->settings; // $this->company->settings = $this->backup_file->company->settings;
$this->company->save(); $this->company->save();
@ -471,6 +475,7 @@ class CompanyImport implements ShouldQueue
$this->company->vendors()->forceDelete(); $this->company->vendors()->forceDelete();
$this->company->expenses()->forceDelete(); $this->company->expenses()->forceDelete();
$this->company->subscriptions()->forceDelete(); $this->company->subscriptions()->forceDelete();
$this->company->purchase_orders()->forceDelete();
$this->company->save(); $this->company->save();
@ -649,6 +654,19 @@ class CompanyImport implements ShouldQueue
return $this; return $this;
} }
private function import_vendor_contacts()
{
$this->genericImport(VendorContact::class,
['user_id', 'company_id', 'id', 'hashed_id','company','assigned_user_id'],
[['users' => 'user_id'], ['vendors' => 'vendor_id']],
'vendor_contacts',
'email');
return $this;
}
private function import_projects() private function import_projects()
{ {
@ -796,6 +814,42 @@ class CompanyImport implements ShouldQueue
return $this; return $this;
} }
private function import_purchase_orders()
{
$this->genericImport(PurchaseOrder::class,
['user_id', 'company_id', 'id', 'hashed_id', 'recurring_id','status', 'vendor_id', 'subscription_id','client_id'],
[
['users' => 'user_id'],
['users' => 'assigned_user_id'],
['recurring_invoices' => 'recurring_id'],
['projects' => 'project_id'],
['vendors' => 'vendor_id'],
],
'purchase_orders',
'number');
return $this;
}
private function import_purchase_order_invitations()
{
$this->genericImport(PurchaseOrderInvitation::class,
['user_id', 'vendor_contact_id', 'company_id', 'id', 'hashed_id', 'purchase_order_id'],
[
['users' => 'user_id'],
['purchase_orders' => 'purchase_order_id'],
['vendor_contacts' => 'vendor_contact_id'],
],
'purchase_order_invitations',
'key');
return $this;
}
private function import_quotes() private function import_quotes()
{ {
@ -1425,6 +1479,13 @@ class CompanyImport implements ShouldQueue
$new_obj->save(['timestamps' => false]); $new_obj->save(['timestamps' => false]);
$new_obj->number = $this->getNextInvoiceNumber($client = Client::withTrashed()->find($obj_array['client_id']),$new_obj); $new_obj->number = $this->getNextInvoiceNumber($client = Client::withTrashed()->find($obj_array['client_id']),$new_obj);
} }
elseif($class == 'App\Models\PurchaseOrder' && is_null($obj->{$match_key})){
$new_obj = new PurchaseOrder();
$new_obj->company_id = $this->company->id;
$new_obj->fill($obj_array);
$new_obj->save(['timestamps' => false]);
$new_obj->number = $this->getNextPurchaseOrderNumber($new_obj);
}
elseif($class == 'App\Models\Payment' && is_null($obj->{$match_key})){ elseif($class == 'App\Models\Payment' && is_null($obj->{$match_key})){
$new_obj = new Payment(); $new_obj = new Payment();
$new_obj->company_id = $this->company->id; $new_obj->company_id = $this->company->id;
@ -1445,6 +1506,12 @@ class CompanyImport implements ShouldQueue
$new_obj->fill($obj_array); $new_obj->fill($obj_array);
$new_obj->save(['timestamps' => false]); $new_obj->save(['timestamps' => false]);
} }
elseif($class == 'App\Models\VendorContact'){
$new_obj = new VendorContact();
$new_obj->company_id = $this->company->id;
$new_obj->fill($obj_array);
$new_obj->save(['timestamps' => false]);
}
elseif($class == 'App\Models\RecurringExpense' && is_null($obj->{$match_key})){ elseif($class == 'App\Models\RecurringExpense' && is_null($obj->{$match_key})){
$new_obj = new RecurringExpense(); $new_obj = new RecurringExpense();
$new_obj->company_id = $this->company->id; $new_obj->company_id = $this->company->id;
@ -1466,6 +1533,13 @@ class CompanyImport implements ShouldQueue
$new_obj->save(['timestamps' => false]); $new_obj->save(['timestamps' => false]);
$new_obj->number = $this->getNextTaskNumber($new_obj); $new_obj->number = $this->getNextTaskNumber($new_obj);
} }
elseif($class == 'App\Models\Vendor' && is_null($obj->{$match_key})){
$new_obj = new Vendor();
$new_obj->company_id = $this->company->id;
$new_obj->fill($obj_array);
$new_obj->save(['timestamps' => false]);
$new_obj->number = $this->getNextVendorNumber($new_obj);
}
elseif($class == 'App\Models\CompanyLedger'){ elseif($class == 'App\Models\CompanyLedger'){
$new_obj = $class::firstOrNew( $new_obj = $class::firstOrNew(
[$match_key => $obj->{$match_key}, 'company_id' => $this->company->id], [$match_key => $obj->{$match_key}, 'company_id' => $this->company->id],

View File

@ -498,8 +498,15 @@ class Account extends BaseModel
$plan_expires = Carbon::parse($this->plan_expires); $plan_expires = Carbon::parse($this->plan_expires);
if(!$this->payment_id && $plan_expires->gt(now())) if(!$this->payment_id && $plan_expires->gt(now())){
return $plan_expires->diffInDays();
$diff = $plan_expires->diffInDays();
if($diff > 14);
return 0;
return $diff;
}
return 0; return 0;
} }

View File

@ -23,10 +23,13 @@ use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str; use Illuminate\Support\Str;
/** /**
* Class BaseModel * Class BaseModel
* *
* @method scope() static * @method scope() static
*
* @package App\Models
*/ */
class BaseModel extends Model class BaseModel extends Model
{ {
@ -187,17 +190,23 @@ class BaseModel extends Model
public function numberFormatter() public function numberFormatter()
{ {
$number = strlen($this->number) >= 1 ? $this->number : class_basename($this).'_'.Str::random(5); $number = strlen($this->number) >= 1 ? $this->number : class_basename($this) . "_" . Str::random(5);
$formatted_number = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $number); $formatted_number = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $number);
// Remove any runs of periods (thanks falstro!) // Remove any runs of periods (thanks falstro!)
$formatted_number = mb_ereg_replace("([\.]{2,})", '', $formatted_number); $formatted_number = mb_ereg_replace("([\.]{2,})", '', $formatted_number);
// $formatted_number = str_replace(" ", "_", $formatted_number); // $formatted_number = str_replace(" ", "_", $formatted_number);
//11-01-2021 fixes for multiple spaces //11-01-2021 fixes for multiple spaces
$formatted_number = preg_replace('/\s+/', '_', $formatted_number); $formatted_number = preg_replace('/\s+/', '_', $formatted_number);
return $formatted_number; return $formatted_number;
} }
public function translate_entity()
{
return ctrans('texts.item');
}
} }

View File

@ -32,7 +32,6 @@ class PurchaseOrder extends BaseModel
protected $fillable = [ protected $fillable = [
'number', 'number',
'discount', 'discount',
'company_id',
'status_id', 'status_id',
'last_sent_date', 'last_sent_date',
'is_deleted', 'is_deleted',
@ -272,4 +271,8 @@ class PurchaseOrder extends BaseModel
return $purchase_order_calc->build(); return $purchase_order_calc->build();
} }
public function translate_entity()
{
return ctrans('texts.purchase_order');
}
} }

View File

@ -309,7 +309,6 @@ class BaseRepository
$model->client->service()->updateBalance(($state['finished_amount'] - $state['starting_amount']))->save(); $model->client->service()->updateBalance(($state['finished_amount'] - $state['starting_amount']))->save();
$model->ledger()->updateInvoiceBalance(($state['finished_amount'] - $state['starting_amount']), "Update adjustment for invoice {$model->number}"); $model->ledger()->updateInvoiceBalance(($state['finished_amount'] - $state['starting_amount']), "Update adjustment for invoice {$model->number}");
} }
if (! $model->design_id) if (! $model->design_id)

View File

@ -5978,6 +5978,10 @@ google_sign_in_platform_interface
google_sign_in_web google_sign_in_web
image_picker_for_web image_picker_for_web
image_picker_platform_interface image_picker_platform_interface
in_app_purchase
in_app_purchase_android
in_app_purchase_platform_interface
in_app_purchase_storekit
local_auth local_auth
package_info package_info
path_provider path_provider

View File

@ -3,43 +3,43 @@ const MANIFEST = 'flutter-app-manifest';
const TEMP = 'flutter-temp-cache'; const TEMP = 'flutter-temp-cache';
const CACHE_NAME = 'flutter-app-cache'; const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = { const RESOURCES = {
"favicon.png": "dca91c54388f52eded692718d5a98b8b", "favicon.ico": "51636d3a390451561744c42188ccd628",
"canvaskit/canvaskit.js": "c2b4e5f3d7a3d82aed024e7249a78487", "manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
"canvaskit/profiling/canvaskit.js": "ae2949af4efc61d28a4a80fffa1db900", "assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
"canvaskit/profiling/canvaskit.wasm": "95e736ab31147d1b2c7b25f11d4c32cd", "assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
"canvaskit/canvaskit.wasm": "4b83d89d9fecbea8ca46f2f760c5a9ba", "assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
"/": "d0d0d12e2fe41ff93c6effa315b26755", "assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3",
"assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a",
"assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541",
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "b62641afc9ab487008e996a5c5865e56",
"assets/NOTICES": "c6e3ca05e75eaf4b48a1de0f34708ab4",
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
"assets/fonts/MaterialIcons-Regular.otf": "95db9098c58fd6db106f1116bae85a0b",
"version.json": "4dfad0f7098e523184a2f58aff0e3940",
"flutter.js": "eb2682e33f25cd8f1fc59011497c35f8", "flutter.js": "eb2682e33f25cd8f1fc59011497c35f8",
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35", "icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed", "icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40", "canvaskit/canvaskit.wasm": "4b83d89d9fecbea8ca46f2f760c5a9ba",
"version.json": "4dfad0f7098e523184a2f58aff0e3940", "canvaskit/canvaskit.js": "c2b4e5f3d7a3d82aed024e7249a78487",
"favicon.ico": "51636d3a390451561744c42188ccd628", "canvaskit/profiling/canvaskit.wasm": "95e736ab31147d1b2c7b25f11d4c32cd",
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f", "canvaskit/profiling/canvaskit.js": "ae2949af4efc61d28a4a80fffa1db900",
"assets/fonts/MaterialIcons-Regular.otf": "95db9098c58fd6db106f1116bae85a0b", "/": "f0472d186e41814a028b5ca7814378c4",
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5", "favicon.png": "dca91c54388f52eded692718d5a98b8b",
"assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541", "main.dart.js": "fe987ef50f1a627038a89c1f4f1dabb0"
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3",
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
"assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a",
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "b62641afc9ab487008e996a5c5865e56",
"assets/NOTICES": "e998160e43be5ffa6f6f6e39b398e093",
"main.dart.js": "4523275a206508c0bf5d6e8ef219cb78"
}; };
// The application shell files that are downloaded before a service worker can // The application shell files that are downloaded before a service worker can

188167
public/main.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

172403
public/main.foss.dart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long