1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-27 03:37:11 +02:00

Merge pull request #6614 from turbo124/v5-stable

v5.3.9
This commit is contained in:
David Bomba 2021-09-10 18:45:15 +10:00 committed by GitHub
commit d114445ab2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 262799 additions and 261866 deletions

View File

@ -1 +1 @@
5.3.8
5.3.9

View File

@ -24,12 +24,14 @@ use App\Models\Invoice;
use App\Models\InvoiceInvitation;
use App\Models\Payment;
use App\Models\Paymentable;
use App\Models\QuoteInvitation;
use App\Models\RecurringInvoiceInvitation;
use App\Utils\Ninja;
use DB;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Mail;
use Symfony\Component\Console\Input\InputOption;
/*
@ -103,6 +105,7 @@ class CheckData extends Command
// $this->checkPaidToCompanyDates();
$this->checkClientBalances();
$this->checkContacts();
$this->checkEntityInvitations();
$this->checkCompanyData();
@ -197,7 +200,7 @@ class CheckData extends Command
->where('id', '=', $contact->id)
->whereNull('contact_key')
->update([
'contact_key' => str_random(config('ninja.key_length')),
'contact_key' => Str::random(config('ninja.key_length')),
]);
}
}
@ -307,13 +310,73 @@ class CheckData extends Command
$invitation->company_id = $invoice->company_id;
$invitation->user_id = $invoice->user_id;
$invitation->invoice_id = $invoice->id;
$invitation->contact_id = ClientContact::whereClientId($invoice->client_id)->whereIsPrimary(true)->first()->id;
$invitation->invitation_key = str_random(config('ninja.key_length'));
$invitation->contact_id = ClientContact::whereClientId($invoice->client_id)->first()->id;
$invitation->invitation_key = Str::random(config('ninja.key_length'));
$invitation->save();
}
}
}
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]);
QuoteInvitation::where('deleted_at',"0000-00-00 00:00:00.000000")->withTrashed()->update(['deleted_at' => null]);
$entities = ['invoice', 'quote', 'credit', 'recurring_invoice'];
foreach($entities as $entity)
{
$table = "{$entity}s";
$invitation_table = "{$entity}_invitations";
$entities = DB::table($table)
->leftJoin($invitation_table, function ($join) use($invitation_table, $table, $entity){
$join->on("{$invitation_table}.{$entity}_id", '=', "{$table}.id");
// ->whereNull("{$invitation_table}.deleted_at");
})
->groupBy("{$table}.id", "{$table}.user_id", "{$table}.company_id", "{$table}.client_id")
->havingRaw("count({$invitation_table}.id) = 0")
->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->fixInvitations($entities, $entity);
}
}
private function fixInvitations($entities, $entity)
{
$entity_key = "{$entity}_id";
$entity_obj = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation';
foreach($entities as $entity)
{
$invitation = new $entity_obj();
$invitation->company_id = $entity->company_id;
$invitation->user_id = $entity->user_id;
$invitation->{$entity_key} = $entity->id;
$invitation->client_contact_id = ClientContact::whereClientId($entity->client_id)->first()->id;
$invitation->key = Str::random(config('ninja.key_length'));
try{
$invitation->save();
}
catch(\Exception $e){
$invitation = null;
}
}
}
// private function checkPaidToCompanyDates()
// {
// Company::cursor()->each(function ($company){

View File

@ -15,12 +15,14 @@ class S3Cleanup extends Command
*/
protected $signature = 'ninja:s3-cleanup';
protected $log = '';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Remove orphan folders';
protected $description = 'Remove orphan folders/files';
/**
* Create a new command instance.
@ -54,7 +56,11 @@ class S3Cleanup extends Command
if(!in_array($dir, $merged))
{
$this->logMessage("Deleting $dir");
/* Ensure we are not deleting the root folder */
if(strlen($dir) > 1)
Storage::disk(config('filesystems.default'))->deleteDirectory($dir);
}
}

View File

@ -71,7 +71,8 @@ class Kernel extends ConsoleKernel
$schedule->job(new AdjustEmailQuota)->dailyAt('23:00')->withoutOverlapping();
$schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
$schedule->command('ninja:check-data --database=db-ninja-02')->daily()->withoutOverlapping();
$schedule->command('ninja:check-data --database=db-ninja-02')->dailyAt('00:15')->withoutOverlapping();
$schedule->command('ninja:s3-cleanup')->dailyAt('23:15')->withoutOverlapping();
}

View File

@ -79,7 +79,7 @@ class DocumentController extends Controller
$zip = new ZipStream(now() . '-documents.zip', $options);
foreach ($documents as $document) {
$zip->addFileFromPath(basename($document->diskPath()), TempFile::path($document->diskPath()));
$zip->addFileFromPath(basename($document->diskPath()), TempFile::path($document->filePath()));
}
$zip->finish();

View File

@ -18,10 +18,10 @@ use App\Libraries\MultiDB;
use App\Models\ClientContact;
use App\Models\Company;
use App\Utils\Ninja;
use Auth;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
class NinjaPlanController extends Controller
{

View File

@ -2,6 +2,7 @@
namespace App\Http\Requests\Gateways\Checkout3ds;
use App\Libraries\MultiDB;
use App\Models\Client;
use App\Models\Company;
use App\Models\CompanyGateway;
@ -37,6 +38,7 @@ class Checkout3dsRequest extends FormRequest
public function getCompany()
{
MultiDB::findAndSetDbByCompanyKey($this->company_key);
return Company::where('company_key', $this->company_key)->first();
}

View File

@ -67,7 +67,7 @@ class InvoiceTransformer extends BaseTransformer {
if ( $transformed['balance'] < $transformed['amount'] ) {
$transformed['payments'] = [[
'date' => date( 'Y-m-d' ),
'date' => isset( $invoice_data['Last Payment Date'] ) ? date( 'Y-m-d', strtotime( $invoice_data['Invoice Date'] ) ) : date( 'Y-m-d' ),
'amount' => $transformed['amount'] - $transformed['balance'],
]];
}
@ -75,3 +75,4 @@ class InvoiceTransformer extends BaseTransformer {
return $transformed;
}
}

View File

@ -121,18 +121,29 @@ class NinjaMailerJob implements ShouldQueue
$message = $e->getMessage();
/**
* Post mark buries the proper message in a a guzzle response
* this merges a text string with a json object
* need to harvest the ->Message property using the following
*/
if($e instanceof ClientException) { //postmark specific failure
$response = $e->getResponse();
$message_body = json_decode($response->getBody()->getContents());
nlog($response);
// $message = $response->Message;
if(property_exists($message_body, 'Message')){
$message = $message_body->Message;
nlog($message);
}
}
/* If the is an entity attached to the message send a failure mailer */
if($this->nmo->entity)
$this->entityEmailFailed($message);
if(Ninja::isHosted() && (!$e instanceof ClientException)) // Don't send postmark failures to Sentry
/* Don't send postmark failures to Sentry */
if(Ninja::isHosted() && (!$e instanceof ClientException))
app('sentry')->captureException($e);
}
}

View File

@ -640,6 +640,7 @@ class Import implements ShouldQueue
$client->updated_at = Carbon::parse($modified['updated_at']);
$client->save(['timestamps' => false]);
$client->fresh();
$client->contacts()->forceDelete();
@ -650,7 +651,7 @@ class Import implements ShouldQueue
$modified_contacts[$key]['company_id'] = $this->company->id;
$modified_contacts[$key]['user_id'] = $this->processUserId($resource);
$modified_contacts[$key]['client_id'] = $client->id;
$modified_contacts[$key]['password'] = 'mysuperpassword'; // @todo, and clean up the code..
$modified_contacts[$key]['password'] = Str::random(8);
unset($modified_contacts[$key]['id']);
}
@ -685,6 +686,8 @@ class Import implements ShouldQueue
'old' => $resource['id'],
'new' => $client->id,
];
$client = null;
}
Client::reguard();

View File

@ -88,7 +88,7 @@ class PaymentNotification implements ShouldQueue
$client = $payment->client;
$amount = $payment->amount;
if ($invoice) {
if ($invoice && $invoice->line_items) {
$items = $invoice->line_items;
$item = end($items)->product_key;
$entity_number = $invoice->number;

View File

@ -62,9 +62,10 @@ class SupportMessageSent extends Mailable
$company = auth()->user()->company();
$user = auth()->user();
$db = str_replace("db-ninja-", "", $company->db);
$is_large = $company->is_large ? "L" : "";
if(Ninja::isHosted())
$subject = "{$priority}Hosted-{$db}-[{$company->is_large}] :: {$plan} :: ".date('M jS, g:ia');
$subject = "{$priority}Hosted-{$db}{$is_large} :: {$plan} :: ".date('M jS, g:ia');
else
$subject = "{$priority}Self Hosted :: {$plan} :: ".date('M jS, g:ia');

View File

@ -126,6 +126,10 @@ class TemplateEmail extends Mailable
if($this->invitation->invoice && $settings->ubl_email_attachment && $this->company->account->hasFeature(Account::FEATURE_DOCUMENTS)){
$ubl_string = CreateUbl::dispatchNow($this->invitation->invoice);
nlog($ubl_string);
if($ubl_string)
$this->attachData($ubl_string, $this->invitation->invoice->getFileName('xml'));
}

View File

@ -90,7 +90,7 @@ class Client extends BaseModel implements HasLocalePreference
'contacts.company',
// 'currency',
// 'primary_contact',
// 'country',
'country',
// 'contacts',
// 'shipping_country',
// 'company',
@ -218,7 +218,7 @@ class Client extends BaseModel implements HasLocalePreference
public function assigned_user()
{
return $this->belongsTo(User::class, 'assigned_user_id', 'id');
return $this->belongsTo(User::class, 'assigned_user_id', 'id')->withTrashed();
}
public function country()

View File

@ -92,7 +92,7 @@ class ClientContact extends Authenticatable implements HasLocalePreference
'custom_value4',
'email',
'is_primary',
'client_id',
// 'client_id',
];
/**

View File

@ -13,10 +13,12 @@ namespace App\Models;
use App\Utils\Traits\MakesDates;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class ClientGatewayToken extends BaseModel
{
use MakesDates;
use SoftDeletes;
protected $casts = [
'meta' => 'object',

View File

@ -36,7 +36,7 @@ class CompanyLedger extends Model
public function user()
{
return $this->belongsTo(User::class);
return $this->belongsTo(User::class)->withTrashed();
}
public function company()

View File

@ -23,6 +23,8 @@ class CompanyToken extends BaseModel
];
protected $with = [
'company',
'user'
];
protected $touches = [];

View File

@ -78,7 +78,7 @@ class CompanyUser extends Pivot
public function user()
{
return $this->belongsTo(User::class);
return $this->belongsTo(User::class)->withTrashed();
}
public function company()

View File

@ -120,7 +120,7 @@ class Credit extends BaseModel
public function assigned_user()
{
return $this->belongsTo(User::class, 'assigned_user_id', 'id');
return $this->belongsTo(User::class, 'assigned_user_id', 'id')->withTrashed();
}
public function history()

View File

@ -84,7 +84,7 @@ class Expense extends BaseModel
public function assigned_user()
{
return $this->belongsTo(User::class, 'assigned_user_id', 'id');
return $this->belongsTo(User::class, 'assigned_user_id', 'id')->withTrashed();
}
public function company()

View File

@ -52,7 +52,7 @@ class GroupSetting extends StaticModel
public function user()
{
return $this->belongsTo(User::class);
return $this->belongsTo(User::class)->withTrashed();
}
public function clients()

View File

@ -45,6 +45,11 @@ class Proposal extends BaseModel
return $this->morphMany(Document::class, 'documentable');
}
public function user()
{
return $this->belongsTo(User::class)->withTrashed();
}
public function assigned_user()
{
return $this->belongsTo(User::class, 'assigned_user_id', 'id');

View File

@ -76,7 +76,7 @@ class Subscription extends BaseModel
public function user(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(User::class);
return $this->belongsTo(User::class)->withTrashed();
}
public function nextDateByInterval($date, $frequency_id)

View File

@ -66,7 +66,7 @@ class Task extends BaseModel
public function user()
{
return $this->belongsTo(User::class);
return $this->belongsTo(User::class)->withTrashed();
}
public function client()

View File

@ -82,7 +82,7 @@ class Vendor extends BaseModel
public function assigned_user()
{
return $this->belongsTo(User::class, 'assigned_user_id', 'id');
return $this->belongsTo(User::class, 'assigned_user_id', 'id')->withTrashed();
}
public function contacts()

View File

@ -87,7 +87,7 @@ class Webhook extends BaseModel
public function user()
{
return $this->belongsTo(User::class);
return $this->belongsTo(User::class)->withTrashed();
}
public function company()

View File

@ -11,6 +11,8 @@
namespace App\Notifications;
use App\Jobs\Invoice\CreateUbl;
use App\Models\Invoice;
use App\Utils\TempFile;
use App\Utils\Traits\MakesInvoiceHtml;
use Illuminate\Bus\Queueable;

View File

@ -46,7 +46,7 @@ class InvoiceObserver
* @return void
*/
public function updated(Invoice $invoice)
{nlog("updated");
{
$subscriptions = Webhook::where('company_id', $invoice->company->id)
->where('event_id', Webhook::EVENT_UPDATE_INVOICE)
->exists();

View File

@ -205,7 +205,6 @@ class BaseDriver extends AbstractPaymentDriver
$invoices->each(function ($invoice) use ($payment) {
event(new InvoiceWasPaid($invoice, $payment, $payment->company, Ninja::eventVars()));
$invoice->service()->workFlow();
});
return $payment->service()->applyNumber()->save();

View File

@ -362,9 +362,14 @@ class StripePaymentDriver extends BaseDriver
$response = null;
try {
$response = $this->stripe
->refunds
->create(['charge' => $payment->transaction_reference, 'amount' => $this->convertToStripeAmount($amount, $this->client->currency()->precision, $this->client->currency())], $meta);
// $response = $this->stripe
// ->refunds
// ->create(['charge' => $payment->transaction_reference, 'amount' => $this->convertToStripeAmount($amount, $this->client->currency()->precision, $this->client->currency())], $meta);
$response = \Stripe\Refund::create([
'charge' => $payment->transaction_reference,
'amount' => $this->convertToStripeAmount($amount, $this->client->currency()->precision, $this->client->currency())
], $meta);
if ($response->status == $response::STATUS_SUCCEEDED) {
SystemLogger::dispatch(['server_response' => $response, 'data' => request()->all(),], SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_STRIPE, $this->client, $this->client->company);

View File

@ -60,12 +60,18 @@ use WePayCommon;
'method' => '1',
*/
try {
$response = $this->wepay_payment_driver->wepay->request('credit_card/authorize', array(
'client_id' => config('ninja.wepay.client_id'),
'client_secret' => config('ninja.wepay.client_secret'),
'credit_card_id' => (int)$data['credit_card_id'],
));
}
catch(\Exception $e){
return $this->wepay_payment_driver->processInternallyFailedPayment($this->wepay_payment_driver, $e);
}
// display the response
// nlog($response);
@ -116,11 +122,16 @@ use WePayCommon;
{
nlog("authorize the card first!");
try {
$response = $this->wepay_payment_driver->wepay->request('credit_card/authorize', array(
'client_id' => config('ninja.wepay.client_id'),
'client_secret' => config('ninja.wepay.client_secret'),
'credit_card_id' => (int)$request->input('credit_card_id'),
));
}
catch(\Exception $e){
return $this->wepay_payment_driver->processInternallyFailedPayment($this->wepay_payment_driver, $e);
}
$credit_card_id = (int)$response->credit_card_id;

View File

@ -56,9 +56,11 @@ class ClientContactRepository extends BaseRepository
if (! $update_contact) {
$update_contact = ClientContactFactory::create($client->company_id, $client->user_id);
$update_contact->client_id = $client->id;
}
//10-09-2021 - enforce the client->id and remove client_id from fillables
$update_contact->client_id = $client->id;
/* We need to set NULL email addresses to blank strings to pass authentication*/
if(array_key_exists('email', $contact) && is_null($contact['email']))
$contact['email'] = '';
@ -88,5 +90,7 @@ class ClientContactRepository extends BaseRepository
$new_contact->email = ' ';
$new_contact->save();
}
$client = null;
}
}

View File

@ -63,8 +63,8 @@ class TriggeredActions extends AbstractService
private function sendEmail()
{
//$reminder_template = $this->invoice->calculateTemplate('invoice');
$reminder_template = 'payment';
$reminder_template = $this->invoice->calculateTemplate('invoice');
//$reminder_template = 'payment';
$this->invoice->invitations->load('contact.client.country', 'invoice.client.country', 'invoice.company')->each(function ($invitation) use ($reminder_template) {
EmailEntity::dispatch($invitation, $this->invoice->company, $reminder_template);

View File

@ -83,6 +83,7 @@ class UpdateInvoicePayment
->updatePaidToDate($paid_amount)
->updateStatus()
->deletePdf()
->workFlow()
->save();
event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));

View File

@ -38,6 +38,7 @@ use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SubscriptionHooker;
use Carbon\Carbon;
use GuzzleHttp\RequestOptions;
use Illuminate\Contracts\Container\BindingResolutionException;
class SubscriptionService
{
@ -950,7 +951,12 @@ class SubscriptionService
return redirect($default_redirect);
}
public function planPaid($invoice)
/**
* @param Invoice $invoice
* @return true
* @throws BindingResolutionException
*/
public function planPaid(Invoice $invoice)
{
$recurring_invoice_hashed_id = $invoice->recurring_invoice()->exists() ? $invoice->recurring_invoice->hashed_id : null;
@ -959,12 +965,14 @@ class SubscriptionService
'subscription' => $this->subscription->hashed_id,
'recurring_invoice' => $recurring_invoice_hashed_id,
'client' => $invoice->client->hashed_id,
'contact' => $invoice->client->primary_contact()->first() ? $invoice->client->contacts->first() : false,
'contact' => $invoice->client->primary_contact()->first() ? $invoice->client->primary_contact()->first(): $invoice->client->contacts->first(),
'invoice' => $invoice->hashed_id,
];
$response = $this->triggerWebhook($context);
nlog($response);
return true;
}
}

View File

@ -14,8 +14,8 @@ return [
'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
'app_version' => '5.3.8',
'app_tag' => '5.3.8',
'app_version' => '5.3.9',
'app_tag' => '5.3.9',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''),

View File

@ -3,12 +3,12 @@ const MANIFEST = 'flutter-app-manifest';
const TEMP = 'flutter-temp-cache';
const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = {
"main.dart.js": "a4ce90340b3e610ee073d2f40c0377c1",
"version.json": "46d4015fc9abcefe5371cafcf2084173",
"main.dart.js": "0124649b1dd42c7fd93b9489eaf99b1e",
"version.json": "9ec5e3813adc4bfd8713556c5059e97d",
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
"/": "7fb4e233bcd97d5af44e8e4ed2d9784b",
"/": "debe3f3301b29dcbdc324fb3f330965a",
"assets/NOTICES": "9eb7e2eb2888ea5bae5f536720db37cd",
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",

172461
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

171966
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

175855
public/main.wasm.dart.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"app_name":"invoiceninja_flutter","version":"5.0.58","build_number":"58"}
{"app_name":"invoiceninja_flutter","version":"5.0.59","build_number":"59"}

View File

@ -36,7 +36,8 @@
}
.company-logo {
max-width: 55%;
height: 100%;
padding-right: 120px;
}
#company-details,
@ -252,6 +253,10 @@
white-space: nowrap;
}
.logo-container {
max-height: 160px;
}
/** Useful snippets, uncomment to enable. **/
/** Hide company logo **/
@ -288,7 +293,9 @@
<tr>
<td>
<div class="header-wrapper" id="header">
<div class="logo-container">
<img class="company-logo" src="$company.logo" alt="$company.name logo"/>
</div>
<div id="company-details"></div>
<div id="company-address"></div>
</div>

View File

@ -208,6 +208,7 @@ trait MockAccountData
$this->cu = CompanyUserFactory::create($user->id, $this->company->id, $this->account->id);
$this->cu->is_owner = true;
$this->cu->is_admin = true;
$this->cu->is_locked = false;
$this->cu->save();
$this->token = \Illuminate\Support\Str::random(64);