Merge branch 'v5-develop' into bank_rules

This commit is contained in:
David Bomba 2022-11-22 23:37:31 +11:00
commit 31c9c0c5a3
34 changed files with 134760 additions and 134517 deletions

View File

@ -1 +1 @@
5.5.41
5.5.42

View File

@ -87,7 +87,8 @@ class AccountTransformer implements AccountTransformerInterface
return [
'id' => $account->id,
'account_type' => $account->CONTAINER,
'account_name' => $account->accountName,
// 'account_name' => $account->accountName,
'account_name' => property_exists($account, 'accountName') ? $account->accountName : $account->nickname,
'account_status' => $account->accountStatus,
'account_number' => property_exists($account, 'accountNumber') ? '**** ' . substr($account?->accountNumber, -7) : '',
'provider_account_id' => $account->providerAccountId,

View File

@ -48,8 +48,15 @@ class EpcQrGenerator
$this->validateFields();
$qr = $writer->writeString($this->encodeMessage());
try {
$qr = $writer->writeString($this->encodeMessage(), 'utf-8');
}
catch(\Throwable $e){
return '';
}
catch(\Exception $e){
return '';
}
return "<svg viewBox='0 0 200 200' width='200' height='200' x='0' y='0' xmlns='http://www.w3.org/2000/svg'>
<rect x='0' y='0' width='100%'' height='100%' />{$qr}</svg>";

View File

@ -87,10 +87,10 @@ class SwissQrGenerator
$qrBill->setUltimateDebtor(
QrBill\DataGroup\Element\StructuredAddress::createWithStreet(
substr($this->client->present()->name(), 0 , 70),
$this->client->address1 ? substr($this->client->address1, 0 , 70) : '',
$this->client->address2 ? substr($this->client->address2, 0 , 16) : '',
$this->client->postal_code ? substr($this->client->postal_code, 0, 16) : '',
$this->client->city ? substr($this->client->city, 0, 35) : '',
$this->client->address1 ? substr($this->client->address1, 0 , 70) : '_',
$this->client->address2 ? substr($this->client->address2, 0 , 16) : '_',
$this->client->postal_code ? substr($this->client->postal_code, 0, 16) : '_',
$this->client->city ? substr($this->client->city, 0, 35) : '_',
'CH'
));

View File

@ -164,7 +164,8 @@ class CompanyController extends BaseController
*/
public function create(CreateCompanyRequest $request)
{
$company = CompanyFactory::create(auth()->user()->company()->account->id);
$cf = new \App\Factory\CompanyFactory;
$company = $cf->create(auth()->user()->company()->account->id);
return $this->itemResponse($company);
}

View File

@ -44,7 +44,7 @@ class StoreBankTransactionRequest extends Request
{
$input = $this->all();
if(array_key_exists('bank_integration_id', $input) && strlen($input['bank_integration_id']) > 1)
if(array_key_exists('bank_integration_id', $input) && strlen($input['bank_integration_id']) > 1 && !is_numeric($input['bank_integration_id']))
$input['bank_integration_id'] = $this->decodePrimaryKey($input['bank_integration_id']);
$this->replace($input);

View File

@ -108,6 +108,8 @@ class UpdateCompanyRequest extends Request
}
}
$settings['email_style_custom'] = str_replace("{{", "", $settings['email_style_custom']);
if (! $account->isFreeHostedClient()) {
return $settings;
}

View File

@ -12,6 +12,7 @@
namespace App\Http\Requests\Report;
use App\Http\Requests\Request;
use Illuminate\Validation\Rule;
class GenericReportRequest extends Request
{
@ -27,11 +28,14 @@ class GenericReportRequest extends Request
public function rules()
{
nlog($this->date_range);
return [
'start_date' => 'string|date',
'end_date' => 'string|date',
'date_key' => 'string',
'date_range' => 'sometimes|string',
'date_range' => 'bail|required|string',
// 'start_date' => [Rule::requiredIf($this->date_range === 'custom')],
// 'end_date' => [Rule::requiredIf($this->date_range === 'custom')],
'end_date' => 'bail|required_if:date_range,custom|nullable|date',
'start_date' => 'bail|required_if:date_range,custom|nullable|date',
'report_keys' => 'present|array',
'send_email' => 'required|bool',
];

View File

@ -28,8 +28,8 @@ class ProfitLossRequest extends Request
public function rules()
{
return [
'start_date' => 'string|date',
'end_date' => 'string|date',
'start_date' => 'required_if:date_range,custom|string|date',
'end_date' => 'required_if:date_range,custom|string|date',
'is_income_billed' => 'required|bail|bool',
'is_expense_billed' => 'bool',
'include_tax' => 'required|bail|bool',

View File

@ -293,7 +293,7 @@ class CompanyExport implements ShouldQueue
$this->export_data['payments'] = $this->company->payments()->orderBy('number', 'DESC')->cursor()->map(function ($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', 'transaction_id']);
$payment->paymentables = $this->transformPaymentable($payment);
@ -456,7 +456,6 @@ class CompanyExport implements ShouldQueue
})->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']);
@ -466,6 +465,21 @@ class CompanyExport implements ShouldQueue
})->all();
$this->export_data['bank_integrations'] = $this->company->bank_integrations()->orderBy('id', 'ASC')->cursor()->map(function ($bank_integration){
$bank_integration = $this->transformArrayOfKeys($bank_integration, ['account_id','company_id', 'user_id']);
return $bank_integration->makeVisible(['id','user_id','company_id','account_id']);
})->all();
$this->export_data['bank_transactions'] = $this->company->bank_transactions()->orderBy('id', 'ASC')->cursor()->map(function ($bank_transaction){
$bank_transaction = $this->transformArrayOfKeys($bank_transaction, ['company_id', 'user_id','bank_integration_id','expense_id','category_id','ninja_category_id','vendor_id']);
return $bank_transaction->makeVisible(['id','user_id','company_id']);
})->all();
//write to tmp and email to owner();
@ -516,9 +530,6 @@ class CompanyExport implements ShouldQueue
$file_name = date('Y-m-d').'_'.str_replace([" ", "/"],["_",""], $this->company->present()->name() . '_' . $this->company->company_key .'.zip');
$path = 'backups';
// if(!Storage::disk(config('filesystems.default'))->exists($path))
// Storage::disk(config('filesystems.default'))->makeDirectory($path, 0775);
$zip_path = public_path('storage/backups/'.$file_name);
$zip = new \ZipArchive();

View File

@ -24,6 +24,8 @@ use App\Mail\Import\CompanyImportFailure;
use App\Mail\Import\ImportCompleted;
use App\Models\Activity;
use App\Models\Backup;
use App\Models\BankIntegration;
use App\Models\BankTransaction;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\ClientGatewayToken;
@ -142,15 +144,16 @@ class CompanyImport implements ShouldQueue
'expenses',
'tasks',
'payments',
// 'activities',
// 'backups',
'company_ledger',
'designs',
'documents',
'webhooks',
'system_logs',
'purchase_orders',
'purchase_order_invitations'
'purchase_order_invitations',
'bank_integrations',
'bank_transactions',
'payments',
];
private $company_properties = [
@ -527,6 +530,37 @@ class CompanyImport implements ShouldQueue
}
private function import_bank_integrations()
{
$this->genericImport(BankIntegration::class,
['assigned_user_id','account_id', 'company_id', 'id', 'hashed_id'],
[
['users' => 'user_id'],
],
'bank_integrations',
'description');
return $this;
}
private function import_bank_transactions()
{
$this->genericImport(BankTransaction::class,
['assigned_user_id','company_id', 'id', 'hashed_id', 'user_id'],
[
['users' => 'user_id'],
['expenses' => 'expense_id'],
['vendors' => 'vendor_id'],
['expense_categories' => 'ninja_category_id'],
['expense_categories' => 'category_id'],
['bank_integrations' => 'bank_integration_id']
],
'bank_transactions',
null);
return $this;
}
private function import_recurring_expenses()
{
//unset / transforms / object_property / match_key
@ -979,6 +1013,7 @@ class CompanyImport implements ShouldQueue
['vendors' => 'vendor_id'],
['invoice_invitations' => 'invitation_id'],
['company_gateways' => 'company_gateway_id'],
['bank_transactions' => 'transaction_id'],
],
'payments',
'number');
@ -1569,6 +1604,28 @@ class CompanyImport implements ShouldQueue
$obj_array,
);
}
elseif($class == 'App\Models\BankIntegration'){
$new_obj = new BankIntegration();
$new_obj->company_id = $this->company->id;
$new_obj->account_id = $this->account->id;
$new_obj->fill($obj_array);
$new_obj->save(['timestamps' => false]);
}
elseif($class == 'App\Models\BankTransaction'){
$new_obj = new BankTransaction();
$new_obj->company_id = $this->company->id;
$obj_array['invoice_ids'] = collect(explode(",",$obj_array['invoice_ids']))->map(function ($id) {
return $this->transformId('invoices', $id);
})->map(function ($encodeable){
return $this->encodePrimaryKey($encodeable);
})->implode(",");
$new_obj->fill($obj_array);
$new_obj->save(['timestamps' => false]);
}
else{
$new_obj = $class::withTrashed()->firstOrNew(
[$match_key => $obj->{$match_key}, 'company_id' => $this->company->id],

View File

@ -129,11 +129,8 @@ class NinjaMailerJob implements ShouldQueue
LightLogs::create(new EmailSuccess($this->nmo->company->company_key))
->send();
// nlog('Using ' . ((int) (memory_get_usage(true) / (1024 * 1024))) . 'MB ');
$this->nmo = null;
$this->company = null;
app('queue.worker')->shouldQuit = 1;
} catch (\Exception | \RuntimeException | \Google\Service\Exception $e) {

View File

@ -19,6 +19,9 @@ use stdClass;
class InvoiceEmailFailedActivity implements ShouldQueue
{
// public $delay = 10;
protected $activity_repo;
/**

View File

@ -18,13 +18,18 @@ use App\Libraries\MultiDB;
use App\Mail\Admin\EntityFailedSendObject;
use App\Notifications\Admin\EntitySentNotification;
use App\Utils\Traits\Notifications\UserNotifies;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class InvoiceFailedEmailNotification
{
use UserNotifies;
public $delay = 5;
use UserNotifies, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $delay = 10;
public function __construct()
{

View File

@ -111,7 +111,10 @@ class PayPal
'paymentMethodNonce' => $gateway_response->nonce,
]);
return $payment_method->paymentMethod->token;
if($payment_method->success)
return $payment_method->paymentMethod->token;
else
throw new PaymentFailed(property_exists($payment_method, 'message') ? $payment_method->message : 'Undefined error storing payment token.', 0);
}
/**

View File

@ -306,10 +306,53 @@ class CheckoutComPaymentDriver extends BaseDriver
try {
$response = $this->gateway->getCustomersClient()->create($request);
} catch (\Exception $e) {
// API error
throw new PaymentFailed($e->getMessage(), $e->getCode());
}
catch (CheckoutApiException $e) {
// API error
$request_id = $e->request_id;
$http_status_code = $e->http_status_code;
$error_details = $e->error_details;
if(is_array($error_details)) {
$error_details = end($e->error_details['error_codes']);
}
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
throw new PaymentFailed($human_exception);
} catch (CheckoutArgumentException $e) {
// Bad arguments
$error_details = $e->error_details;
if(is_array($error_details)) {
$error_details = end($e->error_details['error_codes']);
}
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
throw new PaymentFailed($human_exception);
} catch (CheckoutAuthorizationException $e) {
// Bad Invalid authorization
$error_details = $e->error_details;
if(is_array($error_details)) {
$error_details = end($e->error_details['error_codes']);
}
$human_exception = $error_details ? new \Exception($error_details, 400) : $e;
throw new PaymentFailed($human_exception);
}
// catch (\Exception $e) {
// // API error
// throw new PaymentFailed($e->getMessage(), $e->getCode());
// }
return $response;
}

View File

@ -63,7 +63,11 @@ class ImportCustomers
$this->addCustomer($customer);
}
$starting_after = end($customers->data)['id'];
//handle
if(is_array($customers->data) && end($customers->data) && array_key_exists('id', end($customers->data)))
$starting_after = end($customers->data)['id'];
else
break;
} while ($customers->has_more);
}

View File

@ -451,7 +451,7 @@ class EventServiceProvider extends ServiceProvider
PaymentEmailedActivity::class,
],
PaymentWasEmailedAndFailed::class => [
PaymentEmailFailureActivity::class,
// PaymentEmailFailureActivity::class,
],
PurchaseOrderWasArchived::class => [
PurchaseOrderArchivedActivity::class,

View File

@ -60,7 +60,7 @@ class ActivityRepository extends BaseRepository
$activity->save();
//rate limiter
// $this->createBackup($entity, $activity);
$this->createBackup($entity, $activity);
}
/**

View File

@ -104,7 +104,6 @@ class DeletePayment
$client = $this->payment
->client
->fresh()
->service()
->updateBalance($net_deletable)
->save();
@ -136,9 +135,8 @@ class DeletePayment
});
}
$client = $this->payment->client->fresh();
$client
$this->payment
->client
->service()
->updatePaidToDate(($this->payment->amount - $this->payment->refunded) * -1)
->save();
@ -146,7 +144,7 @@ class DeletePayment
$transaction = [
'invoice' => [],
'payment' => [],
'client' => $client->transaction_event(),
'client' => $this->payment->client->transaction_event(),
'credit' => [],
'metadata' => [],
];

View File

@ -62,12 +62,14 @@ class UpdateInvoicePayment
$paid_amount = $paid_invoice->amount;
}
$client->service()->updateBalanceAndPaidToDate($paid_amount*-1, $paid_amount);
$client->service()->updatePaidToDate($paid_amount); //always use the payment->amount
/* Need to determine here is we have an OVER payment - if YES only apply the max invoice amount */
if($paid_amount > $invoice->partial && $paid_amount > $invoice->balance)
$paid_amount = $invoice->balance;
$client->service()->updateBalance($paid_amount*-1); //only ever use the amount applied to the invoice
/*Improve performance here - 26-01-2022 - also change the order of events for invoice first*/
//caution what if we amount paid was less than partial - we wipe it!
$invoice->balance -= $paid_amount;

View File

@ -40,12 +40,8 @@ use Symfony\Component\HttpFoundation\Request;
class TaskSchedulerService
{
public Scheduler $scheduler;
public function __construct(Scheduler $scheduler)
{
$this->scheduler = $scheduler;
}
public function __construct(public Scheduler $scheduler) {}
public function store(Scheduler $scheduler, CreateScheduledTaskRequest $request)
{

View File

@ -134,6 +134,30 @@ class Helpers
$replacements = [
'literal' => [
':MONTH_BEFORE' => \sprintf(
'%s %s %s',
Carbon::now()->subMonth(1)->translatedFormat($entity->date_format()),
ctrans('texts.to'),
Carbon::now()->subDay(1)->translatedFormat($entity->date_format()),
),
':YEAR_BEFORE' => \sprintf(
'%s %s %s',
Carbon::now()->subYear(1)->translatedFormat($entity->date_format()),
ctrans('texts.to'),
Carbon::now()->subDay(1)->translatedFormat($entity->date_format()),
),
':MONTH_AFTER' => \sprintf(
'%s %s %s',
Carbon::now()->translatedFormat($entity->date_format()),
ctrans('texts.to'),
Carbon::now()->addMonth(1)->subDay(1)->translatedFormat($entity->date_format()),
),
':YEAR_AFTER' => \sprintf(
'%s %s %s',
Carbon::now()->translatedFormat($entity->date_format()),
ctrans('texts.to'),
Carbon::now()->addYear(1)->subDay(1)->translatedFormat($entity->date_format()),
),
':MONTHYEAR' => \sprintf(
'%s %s',
Carbon::createFromDate(now()->month)->translatedFormat('F'),
@ -150,15 +174,15 @@ class Helpers
),
':WEEK_AHEAD' => \sprintf(
'%s %s %s',
Carbon::now()->addDays(6)->translatedFormat($entity->date_format()),
Carbon::now()->addDays(7)->translatedFormat($entity->date_format()),
ctrans('texts.to'),
Carbon::now()->addDays(13)->translatedFormat($entity->date_format())
),
':WEEK' => \sprintf(
'%s %s %s',
Carbon::now()->subDays(7)->translatedFormat($entity->date_format()),
Carbon::now()->translatedFormat($entity->date_format()),
ctrans('texts.to'),
Carbon::now()->addDays(13)->translatedFormat($entity->date_format())
Carbon::now()->addDays(6)->translatedFormat($entity->date_format())
),
],
'raw' => [

View File

@ -95,10 +95,14 @@ class SystemHealth
if(strlen(config('ninja.currency_converter_api_key')) == 0){
try{
$cs = DB::table('clients')
->select('settings->currency_id as id')
->get();
}
catch(\Exception $e){
return true; //fresh installs, there may be no DB connection, nor migrations could have run yet.
}
$currency_count = $cs->unique('id')->filter(function ($value){
return !is_null($value->id);

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.5.41',
'app_tag' => '5.5.41',
'app_version' => '5.5.42',
'app_tag' => '5.5.42',
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''),

View File

@ -11,9 +11,9 @@ const RESOURCES = {
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
"favicon.ico": "51636d3a390451561744c42188ccd628",
"flutter.js": "f85e6fb278b0fd20c349186fb46ae36d",
"/": "112f22769207bffb3936c08dec3ffa4d",
"/": "9d48b2826c07eb42f0817289b6a7b7ca",
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
"main.dart.js": "bddfba2a7d482fece1e7a9ff84429256",
"main.dart.js": "3568e02ff28e4dae78f695088a6e21c5",
"assets/AssetManifest.json": "759f9ef9973f7e26c2a51450b55bb9fa",
"assets/FontManifest.json": "087fb858dc3cbfbf6baf6a30004922f1",
"assets/NOTICES": "1a34e70168d56fad075adfb4bdbb20eb",

131504
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

129170
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

View File

@ -257,7 +257,7 @@
overflow-wrap: break-word;
}
.stamp {
.stamp {
transform: rotate(12deg);
color: #555;
font-size: 3rem;

View File

@ -31,7 +31,7 @@
{{ ctrans('texts.type') }}
</dt>
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{{ $payment_method->meta?->brand }}
{{ property_exists($payment_method->meta, 'brand') ? $payment_method->meta?->brand : ''}}
{{ property_exists($payment_method->meta, 'scheme') ? $payment_method->meta?->scheme : '' }}
</dd>
</div>

View File

@ -186,7 +186,7 @@ Route::group(['middleware' => ['throttle:300,1', 'api_db', 'token_auth', 'locale
Route::put('expenses/{expense}/upload', [ExpenseController::class, 'upload']);
Route::post('expenses/bulk', [ExpenseController::class, 'bulk'])->name('expenses.bulk');
Route::post('export', [ExportController::class, 'index'])->name('export.index');
Route::post('export', [ExportController::class, 'index'])->middleware('throttle:2,1')->name('export.index');
Route::resource('expense_categories', ExpenseCategoryController::class); // name = (expense_categories. index / create / show / update / destroy / edit
Route::post('expense_categories/bulk', [ExpenseCategoryController::class, 'bulk'])->name('expense_categories.bulk');
@ -196,7 +196,7 @@ Route::group(['middleware' => ['throttle:300,1', 'api_db', 'token_auth', 'locale
Route::put('group_settings/{group_setting}/upload', [GroupSettingController::class, 'upload'])->name('group_settings.upload');
Route::post('import', [ImportController::class, 'import'])->name('import.import');
Route::post('import_json', [ImportJsonController::class, 'import'])->name('import.import_json');
Route::post('import_json', [ImportJsonController::class, 'import'])->middleware('throttle:2,1')->name('import.import_json');
Route::post('preimport', [ImportController::class, 'preimport'])->name('import.preimport');
Route::resource('invoices', InvoiceController::class); // name = (invoices. index / create / show / update / destroy / edit