Merge branch 'v5-develop' of https://github.com/invoiceninja/invoiceninja into feature-brevo

This commit is contained in:
paulwer 2024-01-10 12:42:38 +01:00
commit 086fa5bc66
37 changed files with 86035 additions and 85530 deletions

View File

@ -43,7 +43,7 @@ jobs:
run: |
git clone https://${{secrets.commit_secret}}@github.com/invoiceninja/ui.git
cd ui
git checkout main
git checkout develop
npm i
npm run build

View File

@ -1 +1 @@
5.8.0
5.8.8

View File

@ -100,6 +100,31 @@ class CreateSingleAccount extends Command
$this->warmCache();
$this->createSmallAccount();
try {
$pdo = \DB::connection('ronin')->getPdo();
if(class_exists(\Modules\Ronin\app\Models\Admin::class)){
$this->info('Creating Ronin Account');
$this->createRoninAccount();
}
} catch (\Exception $e) {
}
}
private function createRoninAccount()
{
$admin = \Modules\Ronin\app\Models\Admin::create([
'first_name' => 'small',
'last_name' => 'example',
'email' => 'small@example.com',
'password' => Hash::make('password'),
]);
}
private function createSmallAccount()

View File

@ -50,9 +50,9 @@ class S3Cleanup extends Command
*/
public function handle()
{
if (!Ninja::isHosted()) {
return;
}
// if (!Ninja::isHosted()) {
// return;
// }
$c1 = Company::on('db-ninja-01')->pluck('company_key');
$c2 = Company::on('db-ninja-02')->pluck('company_key');

View File

@ -28,7 +28,8 @@ class CloneQuoteToInvoiceFactory
unset($quote_array['invoice_id']);
unset($quote_array['id']);
unset($quote_array['invitations']);
unset($quote_array['user']);
//preserve terms if they exist on Quotes
//if(array_key_exists('terms', $quote_array) && strlen($quote_array['terms']) < 2)
if (! $quote->company->use_quote_terms_on_conversion) {
@ -38,7 +39,6 @@ class CloneQuoteToInvoiceFactory
// unset($quote_array['public_notes']);
unset($quote_array['footer']);
unset($quote_array['design_id']);
unset($quote_array['user']);
foreach ($quote_array as $key => $value) {
$invoice->{$key} = $value;
@ -59,6 +59,7 @@ class CloneQuoteToInvoiceFactory
$invoice->last_sent_date = null;
$invoice->last_viewed = null;
return $invoice;
}
}

View File

@ -115,7 +115,7 @@ class SwissQrGenerator
} else {
$tempInvoiceNumber = $this->invoice->number;
$tempInvoiceNumber = preg_replace('/[^A-Za-z0-9]/', '', $tempInvoiceNumber);
$tempInvoiceNumber = substr($tempInvoiceNumber, 1);
// $tempInvoiceNumber = substr($tempInvoiceNumber, 1);
$calcInvoiceNumber = "";
$array = str_split($tempInvoiceNumber);

View File

@ -36,7 +36,7 @@ class LoginRequest extends Request
public function rules()
{
if (Ninja::isHosted()) {
$email_rules = ['required', new BlackListRule, new EmailBlackListRule];
$email_rules = ['required', new EmailBlackListRule];
} else {
$email_rules = 'required';
}

View File

@ -140,6 +140,10 @@ class BaseImport
$delimiters = [',', '.', ';', '|'];
$bestDelimiter = ',';
$count = 0;
// 10-01-2024 - A better way to resolve the csv file delimiter.
$csvfile = substr($csvfile, 0, strpos($csvfile, "\n"));
foreach ($delimiters as $delimiter) {
if (substr_count(strstr($csvfile, "\n", true), $delimiter) >= $count) {
@ -162,7 +166,6 @@ class BaseImport
private function groupTasks($csvData, $key)
{
nlog($csvData[0]);
if (! $key || !is_array($csvData) || count($csvData) == 0 || !isset($csvData[0]['task.number']) || empty($csvData[0]['task.number'])) {
return $csvData;

View File

@ -64,7 +64,7 @@ class CompanyExport implements ShouldQueue
{
MultiDB::setDb($this->company->db);
$this->company = Company::query()->where('company_key', $this->company->company_key)->first();
// $this->company = Company::query()->where('company_key', $this->company->company_key)->first();
set_time_limit(0);

View File

@ -96,7 +96,8 @@ class BillingPortalPurchasev2 extends Component
*
* @var Invoice
*/
public $invoice;
public \App\Models\Invoice $invoice;
/**
* Coupon model for user input
@ -112,6 +113,9 @@ class BillingPortalPurchasev2 extends Component
*/
public $quantity;
public $invoice_hashed_id = '';
public $payable_amount = 0;
/**
* First-hit request data (queries, locales...).
*
@ -160,7 +164,7 @@ class BillingPortalPurchasev2 extends Component
public $payment_confirmed = false;
public $is_eligible = true;
public $not_eligible_message = '';
public function mount()
{
MultiDB::setDb($this->db);
@ -182,6 +186,9 @@ class BillingPortalPurchasev2 extends Component
$this->sub_total = 0;
$this->float_amount_total = 0;
$this->invoice_hashed_id = '';
$this->payable_amount = 0;
$this->data = [];
$this->price = $this->subscription->price; // ?
@ -493,6 +500,9 @@ class BillingPortalPurchasev2 extends Component
$this->payment_method_id = $gateway_type_id;
$this->handleBeforePaymentEvents();
$this->dispatch('beforePaymentEventsCompleted');
}
/**
@ -500,7 +510,7 @@ class BillingPortalPurchasev2 extends Component
*
* @return self
*/
public function handleBeforePaymentEvents() :self
public function handleBeforePaymentEvents(): self
{
$eligibility_check = $this->subscription->service()->isEligible($this->contact);
@ -520,7 +530,6 @@ class BillingPortalPurchasev2 extends Component
]],
'user_input_promo_code' => $this->coupon,
'coupon' => empty($this->subscription->promo_code) ? '' : $this->coupon,
];
$this->invoice = $this->subscription
@ -532,6 +541,9 @@ class BillingPortalPurchasev2 extends Component
->adjustInventory()
->save();
$this->payable_amount = $this->invoice->partial > 0 ? \App\Utils\Number::formatValue($this->invoice->partial, $this->invoice->client->currency()) : \App\Utils\Number::formatValue($this->invoice->balance, $this->invoice->client->currency());
$this->invoice_hashed_id = $this->invoice->hashed_id;
Cache::put($this->hash, [
'subscription_id' => $this->subscription->hashed_id,
'email' => $this->email ?? $this->contact->email,
@ -542,8 +554,6 @@ class BillingPortalPurchasev2 extends Component
'bundle' => $this->bundle,
], now()->addMinutes(60));
$this->dispatch('beforePaymentEventsCompleted');
return $this;
}

View File

@ -97,11 +97,7 @@ class BaseModel extends Model
public function dateMutator($value)
{
if (! empty($value)) {
return (new Carbon($value))->format('Y-m-d');
}
return $value;
return (new Carbon($value))->format('Y-m-d');
}
// public function __call($method, $params)

View File

@ -545,14 +545,17 @@ class Client extends BaseModel implements HasLocalePreference
$cg = CompanyGateway::query()->find($pm['company_gateway_id']);
if ($cg && ! property_exists($cg->fees_and_limits, strval(GatewayType::CREDIT_CARD))) {
if($cg->gateway_key == '80af24a6a691230bbec33e930ab40666') //ensure we don't attempt to authorize paypal platform - yet.
continue;
if ($cg && is_object($cg->fees_and_limits) && ! property_exists($cg->fees_and_limits, strval(GatewayType::CREDIT_CARD))) {
$fees_and_limits = $cg->fees_and_limits;
$fees_and_limits->{GatewayType::CREDIT_CARD} = new FeesAndLimits;
$cg->fees_and_limits = $fees_and_limits;
$cg->save();
}
if ($cg && $cg->fees_and_limits->{GatewayType::CREDIT_CARD}->is_enabled) {
if ($cg && is_object($cg->fees_and_limits)&& $cg->fees_and_limits->{GatewayType::CREDIT_CARD}->is_enabled) {
return $cg;
}
}
@ -809,6 +812,15 @@ class Client extends BaseModel implements HasLocalePreference
return $defaults;
}
public function setExchangeRate()
{
$converter = new CurrencyApi();
return 1/$converter->convert(1, $this->currency()->id, $this->company->settings->currency_id);
}
public function timezone_offset() :int
{
$offset = 0;

View File

@ -240,12 +240,12 @@ class Invoice extends BaseModel
public function getDueDateAttribute($value)
{
return $this->dateMutator($value);
return $value ? $this->dateMutator($value) : null;
}
public function getPartialDueDateAttribute($value)
{
return $this->dateMutator($value);
return $value ? $this->dateMutator($value) : null;
}
public function company(): \Illuminate\Database\Eloquent\Relations\BelongsTo

View File

@ -107,7 +107,12 @@ class PaymentRepository extends BaseRepository
}
/*Fill the payment*/
$payment->fill($data);
$fill_data = $data;
if($this->import_mode && isset($fill_data['invoices']))
unset($fill_data['invoices']);
$payment->fill($fill_data);
$payment->is_manual = true;
$payment->status_id = Payment::STATUS_COMPLETED;

View File

@ -567,7 +567,7 @@ class InvoiceService
/* If client currency differs from the company default currency, then insert the client exchange rate on the model.*/
if (! isset($this->invoice->exchange_rate) && $this->invoice->client->currency()->id != (int) $this->invoice->company->settings->currency_id) {
$this->invoice->exchange_rate = $this->invoice->client->currency()->exchange_rate;
$this->invoice->exchange_rate = $this->invoice->client->setExchangeRate();
}
if ($this->invoice->client->getSetting('auto_bill_standard_invoices')) {

View File

@ -462,7 +462,7 @@ class PdfMock
'$viewLink' => '<a class="button" href="http://ninja.test:8000/client/invoice/UAUY8vIPuno72igmXbbpldwo5BDDKIqs">View Invoice</a>',
'$autoBill' => 'This invoice will automatically be billed to your credit card on file on the due date.',
'$view_url' => 'http://ninja.test:8000/client/invoice/UAUY8vIPuno72igmXbbpldwo5BDDKIqs',
'$font_url' => 'https://fonts.googleapis.com/css2?family=Roboto&display=swap',
'$font_url' => isset($this->settings?->primary_font) ? \App\Utils\Helpers::resolveFont($this->settings->primary_font)['url'] : 'https://fonts.googleapis.com/css2?family=Roboto&display=swap',
'$details' => '',
'$balance' => '$40.00',
'$partial' => '$30.00',

View File

@ -132,7 +132,7 @@ class InvoiceTransformer extends EntityTransformer
'is_amount_discount' => (bool) ($invoice->is_amount_discount ?: false),
'footer' => $invoice->footer ?: '',
'partial' => (float) ($invoice->partial ?: 0.0),
'partial_due_date' => $invoice->partial_due_date ?: '',
'partial_due_date' => ($invoice->partial_due_date && $invoice->partial_due_date != "-0001-11-30") ? $invoice->partial_due_date : '',
'custom_value1' => (string) $invoice->custom_value1 ?: '',
'custom_value2' => (string) $invoice->custom_value2 ?: '',
'custom_value3' => (string) $invoice->custom_value3 ?: '',

View File

@ -510,13 +510,17 @@ trait GeneratesCounter
private function resetCounters(Client $client)
{
$reset_counter_frequency = (int) $client->getSetting('reset_counter_frequency_id');
$settings_entity = $client->getSettingEntity('reset_counter_frequency_id');
$settings = $settings_entity->settings;
if ($reset_counter_frequency == 0) {
if ($client->getSetting('reset_counter_date')) {
$settings = $client->company->settings;
// $settings = $client->company->settings;
$settings->reset_counter_date = "";
$client->company->settings = $settings;
$client->company->save();
$settings_entity->settings = $settings;
$settings_entity->saveQuietly();
// $client->company->settings = $settings;
// $client->company->save();
}
return;
@ -570,7 +574,7 @@ trait GeneratesCounter
break;
}
$settings = $client->company->settings;
// $settings = $client->company->settings;
$settings->reset_counter_date = $new_reset_date->format('Y-m-d');
$settings->invoice_number_counter = 1;
$settings->quote_number_counter = 1;
@ -583,8 +587,10 @@ trait GeneratesCounter
$settings->recurring_expense_number_counter = 1;
$settings->purchase_order_number_counter = 1;
$client->company->settings = $settings;
$client->company->save();
// $client->company->settings = $settings;
// $client->company->save();
$settings_entity->settings = $settings;
$settings_entity->saveQuietly();
}
private function resetCompanyCounters($company)

View File

@ -182,4 +182,4 @@
],
"minimum-stability": "dev",
"prefer-stable": true
}
}

View File

@ -17,8 +17,8 @@ return [
'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
'app_version' => env('APP_VERSION', '5.7.63'),
'app_tag' => env('APP_TAG', '5.7.63'),
'app_version' => env('APP_VERSION', '5.8.8'),
'app_tag' => env('APP_TAG', '5.8.8'),
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', false),

View File

@ -0,0 +1,55 @@
<?php
use App\Utils\Ninja;
use App\Models\Invoice;
use App\Models\Product;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
if(Ninja::isHosted()) {
return;
}
set_time_limit(0);
Invoice::withTrashed()
->where('is_deleted', false)
->cursor()
->each(function (Invoice $invoice) {
$line_items = $invoice->line_items;
foreach ($line_items as $key => $item)
{
if($product = Product::where('company_id', $invoice->company_id)->where('product_key', $item->product_key)->where('cost', '>', 0)->first())
{
if((property_exists($item, 'product_cost') && $item->product_cost == 0) || !property_exists($item, 'product_cost'))
$line_items[$key]->product_cost = (float)$product->cost;
}
}
$invoice->line_items = $line_items;
$invoice->saveQuietly();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
//
}
};

View File

@ -0,0 +1,53 @@
<?php
use App\Utils\Ninja;
use App\Models\Invoice;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
if(Ninja::isHosted()) {
return;
}
set_time_limit(0);
Invoice::withTrashed()
->where('is_deleted', false)
->cursor()
->each(function (Invoice $invoice) {
$line_items = $invoice->line_items;
foreach ($line_items as $key => $item) {
if(property_exists($item, 'product_cost')) {
$line_items[$key]->product_cost = (float) $line_items[$key]->product_cost;
}
}
$invoice->line_items = $line_items;
$invoice->saveQuietly();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
//
}
};

View File

@ -1,3 +1,4 @@
{
"Admin": true
}
"Admin": true,
"Ronin": true
}

View File

@ -5,7 +5,7 @@ const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = {"flutter.js": "7d69e653079438abfbb24b82a655b0a4",
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
"/": "cb607dac4af69347cb18e4590d38048e",
"/": "6272e8ac86db71cc46f198c0492e95fb",
"assets/AssetManifest.bin": "bf3be26e7055ad9a32f66b3a56138224",
"assets/fonts/MaterialIcons-Regular.otf": "06c266d6fa0b6c2f861f5819063605fa",
"assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a",
@ -296,8 +296,8 @@ const RESOURCES = {"flutter.js": "7d69e653079438abfbb24b82a655b0a4",
"assets/packages/window_manager/images/ic_chrome_minimize.png": "4282cd84cb36edf2efb950ad9269ca62",
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
"favicon.ico": "51636d3a390451561744c42188ccd628",
"main.dart.js": "69534598a5fff62c720b77108c0160e0",
"version.json": "8525697bedeaf8ad4d4d2af0a28fc9ed",
"main.dart.js": "2c362f80fcfbe54f7f07e2e5818c43ab",
"version.json": "5e5238626a11fb360432c45706315af2",
"canvaskit/canvaskit.wasm": "64edb91684bdb3b879812ba2e48dd487",
"canvaskit/skwasm.js": "87063acf45c5e1ab9565dcf06b0c18b8",
"canvaskit/skwasm.wasm": "4124c42a73efa7eb886d3400a1ed7a06",

86870
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

82912
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

@ -1 +1 @@
{"app_name":"invoiceninja_flutter","version":"5.0.147","build_number":"147","package_name":"invoiceninja_flutter"}
{"app_name":"invoiceninja_flutter","version":"5.0.148","build_number":"148","package_name":"invoiceninja_flutter"}

View File

@ -8,10 +8,16 @@
@push('footer')
<script>
function updateGatewayFields(companyGatewayId, paymentMethodId) {
console.log(companyGatewayId, paymentMethodId);
document.getElementById('company_gateway_id').value = companyGatewayId;
document.getElementById('payment_method_id').value = paymentMethodId;
}
Livewire.on('beforePaymentEventsCompleted', () => document.getElementById('payment-method-form').submit());
Livewire.on('beforePaymentEventsCompleted', () => {
setTimeout(() => {
document.getElementById('payment-method-form').submit()
}, 2500);
});
</script>
@endpush

View File

@ -8,27 +8,21 @@
</h1>
</div>
@if(isset($invoice))
<div class="flex items-center mt-4 text-sm">
<form action="{{ route('client.payments.process', ['hash' => $hash, 'sidebar' => 'hidden']) }}"
method="post"
id="payment-method-form">
@csrf
@if($invoice instanceof \App\Models\Invoice)
<input type="hidden" name="invoices[]" value="{{ $invoice->hashed_id }}">
<input type="hidden" name="payable_invoices[0][amount]"
value="{{ $invoice->partial > 0 ? \App\Utils\Number::formatValue($invoice->partial, $invoice->client->currency()) : \App\Utils\Number::formatValue($invoice->balance, $invoice->client->currency()) }}">
<input type="hidden" name="payable_invoices[0][invoice_id]"
value="{{ $invoice->hashed_id }}">
@endif
<input type="hidden" name="invoices[]" value="{{ $invoice_hashed_id }}">
<input type="hidden" name="payable_invoices[0][amount]" value="{{ $payable_amount }}">
<input type="hidden" name="payable_invoices[0][invoice_id]" value="{{ $invoice_hashed_id }}">
<input type="hidden" name="action" value="payment">
<input type="hidden" name="company_gateway_id" value="{{ $company_gateway_id }}"/>
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}"/>
</form>
</div>
@endif
<form wire:submit="submit">
<!-- Recurring Plan Products-->
@ -391,17 +385,68 @@
<form wire:submit="handleLogin" class="" x-data="otpForm()">
<p class="mb-4"></p>
<div class="flex justify-between">
<template x-for="(input, index) in length" :key="index">
<!-- <template x-for="(input, index) in length" :key="index"> -->
<input
id="0"
type="text"
maxlength="1"
class="border border-gray-500 w-10 h-10 text-center text-gray-700"
:x-ref="index"
:x-ref="0"
x-on:input="handleInput($event)"
x-on:paste="handlePaste($event)"
x-on:keydown.backspace="$event.target.value || handleBackspace($event.target.getAttribute('x-ref'))"
/>
</template>
<input
id="1"
type="text"
maxlength="1"
class="border border-gray-500 w-10 h-10 text-center text-gray-700"
:x-ref="1"
x-on:input="handleInput($event)"
x-on:paste="handlePaste($event)"
x-on:keydown.backspace="$event.target.value || handleBackspace($event.target.getAttribute('x-ref'))"
/>
<input
id="2"
type="text"
maxlength="1"
class="border border-gray-500 w-10 h-10 text-center text-gray-700"
:x-ref="2"
x-on:input="handleInput($event)"
x-on:paste="handlePaste($event)"
x-on:keydown.backspace="$event.target.value || handleBackspace($event.target.getAttribute('x-ref'))"
/>
<input
id="3"
type="text"
maxlength="1"
class="border border-gray-500 w-10 h-10 text-center text-gray-700"
:x-ref="3"
x-on:input="handleInput($event)"
x-on:paste="handlePaste($event)"
x-on:keydown.backspace="$event.target.value || handleBackspace($event.target.getAttribute('x-ref'))"
/>
<input
id="4"
type="text"
maxlength="1"
class="border border-gray-500 w-10 h-10 text-center text-gray-700"
:x-ref="4"
x-on:input="handleInput($event)"
x-on:paste="handlePaste($event)"
x-on:keydown.backspace="$event.target.value || handleBackspace($event.target.getAttribute('x-ref'))"
/>
<input
id="5"
type="text"
maxlength="1"
class="border border-gray-500 w-10 h-10 text-center text-gray-700"
:x-ref="5"
x-on:input="handleInput($event)"
x-on:paste="handlePaste($event)"
x-on:keydown.backspace="$event.target.value || handleBackspace($event.target.getAttribute('x-ref'))"
/>
<!-- </template> -->
</div>
</form>
@ -430,7 +475,7 @@
const input = e.target;
this.login = Array.from(Array(this.length), (element, i) => {
return this.$refs[i].value || "";
return document.getElementById(i.toString()).value || '';
}).join("");
if (input.nextElementSibling && input.value) {
@ -451,8 +496,17 @@
const inputs = Array.from(Array(this.length));
inputs.forEach((element, i) => {
this.$refs[i].value = paste[i] || '';
document.getElementById(i.toString()).value = paste[i] || '';
});
this.login = Array.from(Array(this.length), (element, i) => {
return document.getElementById(i.toString()).value || '';
}).join("");
if(this.login.length == 6){
this.$wire.handleLogin(this.login);
}
},
handleBackspace(e) {

View File

@ -11,25 +11,27 @@
namespace Tests\Feature;
use Tests\TestCase;
use App\Models\User;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Account;
use App\Models\Company;
use App\Models\Currency;
use Tests\MockAccountData;
use Illuminate\Support\Str;
use App\Models\CompanyToken;
use App\Models\ClientContact;
use App\Utils\Traits\MakesHash;
use App\DataMapper\ClientSettings;
use App\DataMapper\CompanySettings;
use App\DataMapper\DefaultSettings;
use App\Factory\InvoiceItemFactory;
use App\Models\Account;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Company;
use App\Models\CompanyToken;
use App\Models\Credit;
use App\Models\User;
use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Tests\MockAccountData;
use Tests\TestCase;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Foundation\Testing\DatabaseTransactions;
/**
* @test
@ -43,6 +45,8 @@ class ClientTest extends TestCase
public $faker;
public $client_id;
protected function setUp() :void
{
parent::setUp();
@ -65,6 +69,34 @@ class ClientTest extends TestCase
$this->makeTestData();
}
public function testClientExchangeRateCalculation()
{
$settings = ClientSettings::defaults();
$settings->currency_id = 12;
$c = Client::factory()
->create([
'company_id' => $this->company->id,
'user_id' => $this->user->id,
'settings' => $settings
]);
$settings = $this->company->settings;
$settings->currency_id = '3';
$this->company->saveSettings($settings, $this->company);
$client_exchange_rate = round($c->setExchangeRate(),2);
$aud_currency = Currency::find(12);
$eur_currency = Currency::find(3);
$synthetic_exchange = $aud_currency->exchange_rate / $eur_currency->exchange_rate;
$this->assertEquals($client_exchange_rate, round($synthetic_exchange,2));
}
public function testStoreClientFixes2()
{
$data = [

View File

@ -11,14 +11,16 @@
namespace Tests\Feature;
use App\DataMapper\InvoiceItem;
use Tests\TestCase;
use App\Models\Invoice;
use App\Models\Product;
use Tests\MockAccountData;
use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Support\Facades\Session;
use Tests\MockAccountData;
use Tests\TestCase;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Foundation\Testing\DatabaseTransactions;
/**
* @test
@ -30,6 +32,8 @@ class ProductTest extends TestCase
use DatabaseTransactions;
use MockAccountData;
protected $faker;
protected function setUp() :void
{
parent::setUp();
@ -49,6 +53,75 @@ class ProductTest extends TestCase
}
public function testProductCostMigration()
{
$items = [];
$item = new InvoiceItem();
$item->product_cost = 0;
$item->product_key = 'test';
$item->quantity = 1;
$item->cost = 10;
$item->notes = 'product';
$items[] = $item;
$p = Product::factory()
->create([
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'product_key' => 'test',
'cost' => 10,
'price' => 20,
'quantity' => 1,
'notes' => 'product',
]);
$i = Invoice::factory()
->create([
'client_id' => $this->client->id,
'company_id' => $this->company->id,
'user_id' => $this->user->id,
'line_items' => $items,
]);
$line_items = $i->line_items;
$this->assertEquals(0, $line_items[0]->product_cost);
Invoice::withTrashed()
->where('is_deleted', false)
->cursor()
->each(function (Invoice $invoice) {
$line_items = $invoice->line_items;
foreach ($line_items as $key => $item) {
if($product = Product::where('company_id', $invoice->company_id)->where('product_key', $item->product_key)->where('cost', '>', 0)->first()) {
if((property_exists($item, 'product_cost') && $item->product_cost == 0) || !property_exists($item, 'product_cost')) {
$line_items[$key]->product_cost = $product->cost;
}
}
}
$invoice->line_items = $line_items;
$invoice->saveQuietly();
});
$i = $i->fresh();
$line_items = $i->line_items;
$this->assertEquals(10, $line_items[0]->product_cost);
}
public function testSetTaxId()
{
$p = Product::factory()->create([

View File

@ -397,6 +397,7 @@ class EventTest extends TestCase
];
$quote = Quote::find($this->decodePrimaryKey($arr['data']['id']));
$quote->due_date = now()->addYear();
$quote->status_id = Quote::STATUS_SENT;
$quote->save();

View File

@ -12,6 +12,7 @@
namespace Tests\Unit;
use App\Libraries\Currency\Conversion\CurrencyApi;
use App\Models\Currency;
use Illuminate\Support\Carbon;
use Tests\TestCase;
@ -26,6 +27,21 @@ class CurrencyApiTest extends TestCase
parent::setUp();
}
public function testConversionAudToEur()
{
$converter = new CurrencyApi();
$converted_amount = $converter->convert(100, 12, 3);
$aud_currency = Currency::find(12);
$eur_currency = Currency::find(3);
$converted_synthetic = 100 / ($aud_currency->exchange_rate / $eur_currency->exchange_rate);
$this->assertEquals(round($converted_synthetic, 2), round($converted_amount, 2));
}
public function testCurrencyConversionWorking()
{
$converter = new CurrencyApi();

View File

@ -11,23 +11,24 @@
namespace Tests\Unit;
use App\DataMapper\ClientSettings;
use Tests\TestCase;
use App\Models\Quote;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Company;
use App\Models\Invoice;
use App\Models\Timezone;
use Tests\MockAccountData;
use App\Models\GroupSetting;
use App\Factory\ClientFactory;
use App\Factory\VendorFactory;
use App\Models\Client;
use App\Models\Company;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Quote;
use App\Models\RecurringInvoice;
use App\Models\Timezone;
use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesHash;
use App\Models\RecurringInvoice;
use App\DataMapper\ClientSettings;
use App\Utils\Traits\GeneratesCounter;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Session;
use Tests\MockAccountData;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseTransactions;
/**
* @test
@ -53,6 +54,121 @@ class GeneratesCounterTest extends TestCase
$this->makeTestData();
}
public function testResetCounterGroup()
{
$timezone = Timezone::find(1);
$date_formatted = now($timezone->name)->format('Ymd');
$gs = new GroupSetting;
$gs->name = 'Test';
$gs->company_id = $this->client->company_id;
$gs->settings = ClientSettings::buildClientSettings($this->company->settings, $this->client->settings);
$gs->save();
$this->client->group_settings_id = $gs->id;
$this->client->save();
$settings = $gs->settings;
// $settings = $this->client->settings;
$settings->invoice_number_pattern = '{$date:Ymd}-{$group_counter}';
$settings->timezone_id = 1;
$gs->settings = $settings;
$gs->save();
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($date_formatted.'-0001', $invoice_number);
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($date_formatted.'-0002', $invoice_number);
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($date_formatted.'-0003', $invoice_number);
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($date_formatted.'-0004', $invoice_number);
$settings->reset_counter_date = now($timezone->name)->format('Y-m-d');
$settings->reset_counter_frequency_id = RecurringInvoice::FREQUENCY_DAILY;
$gs->settings = $settings;
$gs->save();
$this->travel(5)->days();
$date_formatted = now($timezone->name)->format('Ymd');
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($date_formatted.'-0001', $invoice_number);
$this->invoice->number = $invoice_number;
$this->invoice->save();
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($date_formatted.'-0002', $invoice_number);
$settings->reset_counter_date = now($timezone->name)->format('Y-m-d');
$settings->reset_counter_frequency_id = RecurringInvoice::FREQUENCY_DAILY;
$gs->settings = $settings;
$gs->save();
$this->travel(5)->days();
$date_formatted = now($timezone->name)->format('Ymd');
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($date_formatted.'-0001', $invoice_number);
$this->travelBack();
}
public function testResetCounterClient()
{
$timezone = Timezone::find(1);
$date_formatted = now($timezone->name)->format('Ymd');
$settings = $this->client->settings;
$settings->invoice_number_pattern = '{$date:Ymd}-{$client_counter}';
$settings->timezone_id = 1;
$this->client->settings = $settings;
$this->client->save();
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($date_formatted.'-0001', $invoice_number);
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($date_formatted.'-0002', $invoice_number);
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($date_formatted.'-0003', $invoice_number);
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($date_formatted.'-0004', $invoice_number);
$settings->reset_counter_date = now($timezone->name)->format('Y-m-d');
$settings->reset_counter_frequency_id = RecurringInvoice::FREQUENCY_DAILY;
$this->client->settings = $settings;
$this->client->save();
$this->travel(5)->days();
$date_formatted = now($timezone->name)->format('Ymd');
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($date_formatted.'-0001', $invoice_number);
$this->invoice->number = $invoice_number;
$this->invoice->save();
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($date_formatted.'-0002', $invoice_number);
$settings->reset_counter_date = now($timezone->name)->format('Y-m-d');
$settings->reset_counter_frequency_id = RecurringInvoice::FREQUENCY_DAILY;
$this->client->settings = $settings;
$this->client->save();
$this->travel(5)->days();
$date_formatted = now($timezone->name)->format('Ymd');
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($date_formatted.'-0001', $invoice_number);
$this->travelBack();
}
public function testResetCounter()
{
$timezone = Timezone::find(1);
@ -82,8 +198,8 @@ class GeneratesCounterTest extends TestCase
$this->company->settings = $settings;
$this->company->save();
$this->client->settings = $settings;
$this->client->save();
// $this->client->settings = $settings;
// $this->client->save();
$this->travel(5)->days();
$date_formatted = now($timezone->name)->format('Ymd');