mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge branch 'v5-develop' of https://github.com/invoiceninja/invoiceninja into feature-brevo
This commit is contained in:
commit
086fa5bc66
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -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
|
||||
|
||||
|
@ -1 +1 @@
|
||||
5.8.0
|
||||
5.8.8
|
@ -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()
|
||||
|
@ -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');
|
||||
|
@ -28,6 +28,7 @@ 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)
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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';
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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...).
|
||||
*
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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')) {
|
||||
|
@ -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',
|
||||
|
@ -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 ?: '',
|
||||
|
@ -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)
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
{
|
||||
//
|
||||
}
|
||||
};
|
@ -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
|
||||
{
|
||||
//
|
||||
}
|
||||
};
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"Admin": true
|
||||
"Admin": true,
|
||||
"Ronin": true
|
||||
}
|
6
public/flutter_service_worker.js
vendored
6
public/flutter_service_worker.js
vendored
@ -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
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
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
1116
public/main.profile.dart.js
vendored
1116
public/main.profile.dart.js
vendored
File diff suppressed because one or more lines are too long
@ -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"}
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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 = [
|
||||
|
@ -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([
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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');
|
||||
|
Loading…
x
Reference in New Issue
Block a user