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: |
|
run: |
|
||||||
git clone https://${{secrets.commit_secret}}@github.com/invoiceninja/ui.git
|
git clone https://${{secrets.commit_secret}}@github.com/invoiceninja/ui.git
|
||||||
cd ui
|
cd ui
|
||||||
git checkout main
|
git checkout develop
|
||||||
npm i
|
npm i
|
||||||
npm run build
|
npm run build
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
5.8.0
|
5.8.8
|
@ -100,6 +100,31 @@ class CreateSingleAccount extends Command
|
|||||||
$this->warmCache();
|
$this->warmCache();
|
||||||
|
|
||||||
$this->createSmallAccount();
|
$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()
|
private function createSmallAccount()
|
||||||
|
@ -50,9 +50,9 @@ class S3Cleanup extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
if (!Ninja::isHosted()) {
|
// if (!Ninja::isHosted()) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
$c1 = Company::on('db-ninja-01')->pluck('company_key');
|
$c1 = Company::on('db-ninja-01')->pluck('company_key');
|
||||||
$c2 = Company::on('db-ninja-02')->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['invoice_id']);
|
||||||
unset($quote_array['id']);
|
unset($quote_array['id']);
|
||||||
unset($quote_array['invitations']);
|
unset($quote_array['invitations']);
|
||||||
|
unset($quote_array['user']);
|
||||||
|
|
||||||
//preserve terms if they exist on Quotes
|
//preserve terms if they exist on Quotes
|
||||||
//if(array_key_exists('terms', $quote_array) && strlen($quote_array['terms']) < 2)
|
//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['public_notes']);
|
||||||
unset($quote_array['footer']);
|
unset($quote_array['footer']);
|
||||||
unset($quote_array['design_id']);
|
unset($quote_array['design_id']);
|
||||||
unset($quote_array['user']);
|
|
||||||
|
|
||||||
foreach ($quote_array as $key => $value) {
|
foreach ($quote_array as $key => $value) {
|
||||||
$invoice->{$key} = $value;
|
$invoice->{$key} = $value;
|
||||||
@ -59,6 +59,7 @@ class CloneQuoteToInvoiceFactory
|
|||||||
$invoice->last_sent_date = null;
|
$invoice->last_sent_date = null;
|
||||||
$invoice->last_viewed = null;
|
$invoice->last_viewed = null;
|
||||||
|
|
||||||
|
|
||||||
return $invoice;
|
return $invoice;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ class SwissQrGenerator
|
|||||||
} else {
|
} else {
|
||||||
$tempInvoiceNumber = $this->invoice->number;
|
$tempInvoiceNumber = $this->invoice->number;
|
||||||
$tempInvoiceNumber = preg_replace('/[^A-Za-z0-9]/', '', $tempInvoiceNumber);
|
$tempInvoiceNumber = preg_replace('/[^A-Za-z0-9]/', '', $tempInvoiceNumber);
|
||||||
$tempInvoiceNumber = substr($tempInvoiceNumber, 1);
|
// $tempInvoiceNumber = substr($tempInvoiceNumber, 1);
|
||||||
|
|
||||||
$calcInvoiceNumber = "";
|
$calcInvoiceNumber = "";
|
||||||
$array = str_split($tempInvoiceNumber);
|
$array = str_split($tempInvoiceNumber);
|
||||||
|
@ -36,7 +36,7 @@ class LoginRequest extends Request
|
|||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
if (Ninja::isHosted()) {
|
if (Ninja::isHosted()) {
|
||||||
$email_rules = ['required', new BlackListRule, new EmailBlackListRule];
|
$email_rules = ['required', new EmailBlackListRule];
|
||||||
} else {
|
} else {
|
||||||
$email_rules = 'required';
|
$email_rules = 'required';
|
||||||
}
|
}
|
||||||
|
@ -140,6 +140,10 @@ class BaseImport
|
|||||||
$delimiters = [',', '.', ';', '|'];
|
$delimiters = [',', '.', ';', '|'];
|
||||||
$bestDelimiter = ',';
|
$bestDelimiter = ',';
|
||||||
$count = 0;
|
$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) {
|
foreach ($delimiters as $delimiter) {
|
||||||
|
|
||||||
if (substr_count(strstr($csvfile, "\n", true), $delimiter) >= $count) {
|
if (substr_count(strstr($csvfile, "\n", true), $delimiter) >= $count) {
|
||||||
@ -162,7 +166,6 @@ class BaseImport
|
|||||||
|
|
||||||
private function groupTasks($csvData, $key)
|
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'])) {
|
if (! $key || !is_array($csvData) || count($csvData) == 0 || !isset($csvData[0]['task.number']) || empty($csvData[0]['task.number'])) {
|
||||||
return $csvData;
|
return $csvData;
|
||||||
|
@ -64,7 +64,7 @@ class CompanyExport implements ShouldQueue
|
|||||||
{
|
{
|
||||||
MultiDB::setDb($this->company->db);
|
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);
|
set_time_limit(0);
|
||||||
|
|
||||||
|
@ -96,7 +96,8 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
*
|
*
|
||||||
* @var Invoice
|
* @var Invoice
|
||||||
*/
|
*/
|
||||||
public $invoice;
|
|
||||||
|
public \App\Models\Invoice $invoice;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Coupon model for user input
|
* Coupon model for user input
|
||||||
@ -112,6 +113,9 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
*/
|
*/
|
||||||
public $quantity;
|
public $quantity;
|
||||||
|
|
||||||
|
public $invoice_hashed_id = '';
|
||||||
|
|
||||||
|
public $payable_amount = 0;
|
||||||
/**
|
/**
|
||||||
* First-hit request data (queries, locales...).
|
* First-hit request data (queries, locales...).
|
||||||
*
|
*
|
||||||
@ -182,6 +186,9 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
$this->sub_total = 0;
|
$this->sub_total = 0;
|
||||||
$this->float_amount_total = 0;
|
$this->float_amount_total = 0;
|
||||||
|
|
||||||
|
$this->invoice_hashed_id = '';
|
||||||
|
$this->payable_amount = 0;
|
||||||
|
|
||||||
$this->data = [];
|
$this->data = [];
|
||||||
|
|
||||||
$this->price = $this->subscription->price; // ?
|
$this->price = $this->subscription->price; // ?
|
||||||
@ -493,6 +500,9 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
$this->payment_method_id = $gateway_type_id;
|
$this->payment_method_id = $gateway_type_id;
|
||||||
|
|
||||||
$this->handleBeforePaymentEvents();
|
$this->handleBeforePaymentEvents();
|
||||||
|
|
||||||
|
$this->dispatch('beforePaymentEventsCompleted');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -520,7 +530,6 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
]],
|
]],
|
||||||
'user_input_promo_code' => $this->coupon,
|
'user_input_promo_code' => $this->coupon,
|
||||||
'coupon' => empty($this->subscription->promo_code) ? '' : $this->coupon,
|
'coupon' => empty($this->subscription->promo_code) ? '' : $this->coupon,
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->invoice = $this->subscription
|
$this->invoice = $this->subscription
|
||||||
@ -532,6 +541,9 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
->adjustInventory()
|
->adjustInventory()
|
||||||
->save();
|
->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, [
|
Cache::put($this->hash, [
|
||||||
'subscription_id' => $this->subscription->hashed_id,
|
'subscription_id' => $this->subscription->hashed_id,
|
||||||
'email' => $this->email ?? $this->contact->email,
|
'email' => $this->email ?? $this->contact->email,
|
||||||
@ -542,8 +554,6 @@ class BillingPortalPurchasev2 extends Component
|
|||||||
'bundle' => $this->bundle,
|
'bundle' => $this->bundle,
|
||||||
], now()->addMinutes(60));
|
], now()->addMinutes(60));
|
||||||
|
|
||||||
$this->dispatch('beforePaymentEventsCompleted');
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,13 +97,9 @@ class BaseModel extends Model
|
|||||||
|
|
||||||
public function dateMutator($value)
|
public function dateMutator($value)
|
||||||
{
|
{
|
||||||
if (! empty($value)) {
|
|
||||||
return (new Carbon($value))->format('Y-m-d');
|
return (new Carbon($value))->format('Y-m-d');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// public function __call($method, $params)
|
// public function __call($method, $params)
|
||||||
// {
|
// {
|
||||||
// $entity = strtolower(class_basename($this));
|
// $entity = strtolower(class_basename($this));
|
||||||
|
@ -545,14 +545,17 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
|
|
||||||
$cg = CompanyGateway::query()->find($pm['company_gateway_id']);
|
$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 = $cg->fees_and_limits;
|
||||||
$fees_and_limits->{GatewayType::CREDIT_CARD} = new FeesAndLimits;
|
$fees_and_limits->{GatewayType::CREDIT_CARD} = new FeesAndLimits;
|
||||||
$cg->fees_and_limits = $fees_and_limits;
|
$cg->fees_and_limits = $fees_and_limits;
|
||||||
$cg->save();
|
$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;
|
return $cg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -809,6 +812,15 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
return $defaults;
|
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
|
public function timezone_offset() :int
|
||||||
{
|
{
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
|
@ -240,12 +240,12 @@ class Invoice extends BaseModel
|
|||||||
|
|
||||||
public function getDueDateAttribute($value)
|
public function getDueDateAttribute($value)
|
||||||
{
|
{
|
||||||
return $this->dateMutator($value);
|
return $value ? $this->dateMutator($value) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPartialDueDateAttribute($value)
|
public function getPartialDueDateAttribute($value)
|
||||||
{
|
{
|
||||||
return $this->dateMutator($value);
|
return $value ? $this->dateMutator($value) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function company(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
public function company(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
@ -107,7 +107,12 @@ class PaymentRepository extends BaseRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*Fill the payment*/
|
/*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->is_manual = true;
|
||||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
$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 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) {
|
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')) {
|
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>',
|
'$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.',
|
'$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',
|
'$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' => '',
|
'$details' => '',
|
||||||
'$balance' => '$40.00',
|
'$balance' => '$40.00',
|
||||||
'$partial' => '$30.00',
|
'$partial' => '$30.00',
|
||||||
|
@ -132,7 +132,7 @@ class InvoiceTransformer extends EntityTransformer
|
|||||||
'is_amount_discount' => (bool) ($invoice->is_amount_discount ?: false),
|
'is_amount_discount' => (bool) ($invoice->is_amount_discount ?: false),
|
||||||
'footer' => $invoice->footer ?: '',
|
'footer' => $invoice->footer ?: '',
|
||||||
'partial' => (float) ($invoice->partial ?: 0.0),
|
'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_value1' => (string) $invoice->custom_value1 ?: '',
|
||||||
'custom_value2' => (string) $invoice->custom_value2 ?: '',
|
'custom_value2' => (string) $invoice->custom_value2 ?: '',
|
||||||
'custom_value3' => (string) $invoice->custom_value3 ?: '',
|
'custom_value3' => (string) $invoice->custom_value3 ?: '',
|
||||||
|
@ -510,13 +510,17 @@ trait GeneratesCounter
|
|||||||
private function resetCounters(Client $client)
|
private function resetCounters(Client $client)
|
||||||
{
|
{
|
||||||
$reset_counter_frequency = (int) $client->getSetting('reset_counter_frequency_id');
|
$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 ($reset_counter_frequency == 0) {
|
||||||
if ($client->getSetting('reset_counter_date')) {
|
if ($client->getSetting('reset_counter_date')) {
|
||||||
$settings = $client->company->settings;
|
// $settings = $client->company->settings;
|
||||||
$settings->reset_counter_date = "";
|
$settings->reset_counter_date = "";
|
||||||
$client->company->settings = $settings;
|
$settings_entity->settings = $settings;
|
||||||
$client->company->save();
|
$settings_entity->saveQuietly();
|
||||||
|
// $client->company->settings = $settings;
|
||||||
|
// $client->company->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -570,7 +574,7 @@ trait GeneratesCounter
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$settings = $client->company->settings;
|
// $settings = $client->company->settings;
|
||||||
$settings->reset_counter_date = $new_reset_date->format('Y-m-d');
|
$settings->reset_counter_date = $new_reset_date->format('Y-m-d');
|
||||||
$settings->invoice_number_counter = 1;
|
$settings->invoice_number_counter = 1;
|
||||||
$settings->quote_number_counter = 1;
|
$settings->quote_number_counter = 1;
|
||||||
@ -583,8 +587,10 @@ trait GeneratesCounter
|
|||||||
$settings->recurring_expense_number_counter = 1;
|
$settings->recurring_expense_number_counter = 1;
|
||||||
$settings->purchase_order_number_counter = 1;
|
$settings->purchase_order_number_counter = 1;
|
||||||
|
|
||||||
$client->company->settings = $settings;
|
// $client->company->settings = $settings;
|
||||||
$client->company->save();
|
// $client->company->save();
|
||||||
|
$settings_entity->settings = $settings;
|
||||||
|
$settings_entity->saveQuietly();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function resetCompanyCounters($company)
|
private function resetCompanyCounters($company)
|
||||||
|
@ -17,8 +17,8 @@ return [
|
|||||||
'require_https' => env('REQUIRE_HTTPS', true),
|
'require_https' => env('REQUIRE_HTTPS', true),
|
||||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||||
'app_version' => env('APP_VERSION', '5.7.63'),
|
'app_version' => env('APP_VERSION', '5.8.8'),
|
||||||
'app_tag' => env('APP_TAG', '5.7.63'),
|
'app_tag' => env('APP_TAG', '5.8.8'),
|
||||||
'minimum_client_version' => '5.0.16',
|
'minimum_client_version' => '5.0.16',
|
||||||
'terms_version' => '1.0.1',
|
'terms_version' => '1.0.1',
|
||||||
'api_secret' => env('API_SECRET', false),
|
'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",
|
const RESOURCES = {"flutter.js": "7d69e653079438abfbb24b82a655b0a4",
|
||||||
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
|
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
|
||||||
"/": "cb607dac4af69347cb18e4590d38048e",
|
"/": "6272e8ac86db71cc46f198c0492e95fb",
|
||||||
"assets/AssetManifest.bin": "bf3be26e7055ad9a32f66b3a56138224",
|
"assets/AssetManifest.bin": "bf3be26e7055ad9a32f66b3a56138224",
|
||||||
"assets/fonts/MaterialIcons-Regular.otf": "06c266d6fa0b6c2f861f5819063605fa",
|
"assets/fonts/MaterialIcons-Regular.otf": "06c266d6fa0b6c2f861f5819063605fa",
|
||||||
"assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a",
|
"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",
|
"assets/packages/window_manager/images/ic_chrome_minimize.png": "4282cd84cb36edf2efb950ad9269ca62",
|
||||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
||||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||||
"main.dart.js": "69534598a5fff62c720b77108c0160e0",
|
"main.dart.js": "2c362f80fcfbe54f7f07e2e5818c43ab",
|
||||||
"version.json": "8525697bedeaf8ad4d4d2af0a28fc9ed",
|
"version.json": "5e5238626a11fb360432c45706315af2",
|
||||||
"canvaskit/canvaskit.wasm": "64edb91684bdb3b879812ba2e48dd487",
|
"canvaskit/canvaskit.wasm": "64edb91684bdb3b879812ba2e48dd487",
|
||||||
"canvaskit/skwasm.js": "87063acf45c5e1ab9565dcf06b0c18b8",
|
"canvaskit/skwasm.js": "87063acf45c5e1ab9565dcf06b0c18b8",
|
||||||
"canvaskit/skwasm.wasm": "4124c42a73efa7eb886d3400a1ed7a06",
|
"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
1112
public/main.profile.dart.js
vendored
1112
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')
|
@push('footer')
|
||||||
<script>
|
<script>
|
||||||
function updateGatewayFields(companyGatewayId, paymentMethodId) {
|
function updateGatewayFields(companyGatewayId, paymentMethodId) {
|
||||||
|
console.log(companyGatewayId, paymentMethodId);
|
||||||
|
|
||||||
document.getElementById('company_gateway_id').value = companyGatewayId;
|
document.getElementById('company_gateway_id').value = companyGatewayId;
|
||||||
document.getElementById('payment_method_id').value = paymentMethodId;
|
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>
|
</script>
|
||||||
@endpush
|
@endpush
|
||||||
|
@ -8,27 +8,21 @@
|
|||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if(isset($invoice))
|
|
||||||
<div class="flex items-center mt-4 text-sm">
|
<div class="flex items-center mt-4 text-sm">
|
||||||
<form action="{{ route('client.payments.process', ['hash' => $hash, 'sidebar' => 'hidden']) }}"
|
<form action="{{ route('client.payments.process', ['hash' => $hash, 'sidebar' => 'hidden']) }}"
|
||||||
method="post"
|
method="post"
|
||||||
id="payment-method-form">
|
id="payment-method-form">
|
||||||
@csrf
|
@csrf
|
||||||
|
|
||||||
@if($invoice instanceof \App\Models\Invoice)
|
<input type="hidden" name="invoices[]" value="{{ $invoice_hashed_id }}">
|
||||||
<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][amount]"
|
<input type="hidden" name="payable_invoices[0][invoice_id]" value="{{ $invoice_hashed_id }}">
|
||||||
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="action" value="payment">
|
<input type="hidden" name="action" value="payment">
|
||||||
<input type="hidden" name="company_gateway_id" value="{{ $company_gateway_id }}"/>
|
<input type="hidden" name="company_gateway_id" value="{{ $company_gateway_id }}"/>
|
||||||
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}"/>
|
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}"/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
|
||||||
|
|
||||||
<form wire:submit="submit">
|
<form wire:submit="submit">
|
||||||
<!-- Recurring Plan Products-->
|
<!-- Recurring Plan Products-->
|
||||||
@ -391,17 +385,68 @@
|
|||||||
<form wire:submit="handleLogin" class="" x-data="otpForm()">
|
<form wire:submit="handleLogin" class="" x-data="otpForm()">
|
||||||
<p class="mb-4"></p>
|
<p class="mb-4"></p>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<template x-for="(input, index) in length" :key="index">
|
<!-- <template x-for="(input, index) in length" :key="index"> -->
|
||||||
<input
|
<input
|
||||||
|
id="0"
|
||||||
type="text"
|
type="text"
|
||||||
maxlength="1"
|
maxlength="1"
|
||||||
class="border border-gray-500 w-10 h-10 text-center text-gray-700"
|
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:input="handleInput($event)"
|
||||||
x-on:paste="handlePaste($event)"
|
x-on:paste="handlePaste($event)"
|
||||||
x-on:keydown.backspace="$event.target.value || handleBackspace($event.target.getAttribute('x-ref'))"
|
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>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
@ -430,7 +475,7 @@
|
|||||||
const input = e.target;
|
const input = e.target;
|
||||||
|
|
||||||
this.login = Array.from(Array(this.length), (element, i) => {
|
this.login = Array.from(Array(this.length), (element, i) => {
|
||||||
return this.$refs[i].value || "";
|
return document.getElementById(i.toString()).value || '';
|
||||||
}).join("");
|
}).join("");
|
||||||
|
|
||||||
if (input.nextElementSibling && input.value) {
|
if (input.nextElementSibling && input.value) {
|
||||||
@ -451,8 +496,17 @@
|
|||||||
const inputs = Array.from(Array(this.length));
|
const inputs = Array.from(Array(this.length));
|
||||||
|
|
||||||
inputs.forEach((element, i) => {
|
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) {
|
handleBackspace(e) {
|
||||||
|
@ -11,25 +11,27 @@
|
|||||||
|
|
||||||
namespace Tests\Feature;
|
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\CompanySettings;
|
||||||
use App\DataMapper\DefaultSettings;
|
use App\DataMapper\DefaultSettings;
|
||||||
use App\Factory\InvoiceItemFactory;
|
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\Database\Eloquent\Model;
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|
||||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
|
||||||
use Illuminate\Support\Facades\Session;
|
use Illuminate\Support\Facades\Session;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
use Tests\MockAccountData;
|
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||||
use Tests\TestCase;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
@ -43,6 +45,8 @@ class ClientTest extends TestCase
|
|||||||
|
|
||||||
public $faker;
|
public $faker;
|
||||||
|
|
||||||
|
public $client_id;
|
||||||
|
|
||||||
protected function setUp() :void
|
protected function setUp() :void
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
@ -65,6 +69,34 @@ class ClientTest extends TestCase
|
|||||||
$this->makeTestData();
|
$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()
|
public function testStoreClientFixes2()
|
||||||
{
|
{
|
||||||
$data = [
|
$data = [
|
||||||
|
@ -11,14 +11,16 @@
|
|||||||
|
|
||||||
namespace Tests\Feature;
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\DataMapper\InvoiceItem;
|
||||||
|
use Tests\TestCase;
|
||||||
|
use App\Models\Invoice;
|
||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
|
use Tests\MockAccountData;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|
||||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
|
||||||
use Illuminate\Support\Facades\Session;
|
use Illuminate\Support\Facades\Session;
|
||||||
use Tests\MockAccountData;
|
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||||
use Tests\TestCase;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
@ -30,6 +32,8 @@ class ProductTest extends TestCase
|
|||||||
use DatabaseTransactions;
|
use DatabaseTransactions;
|
||||||
use MockAccountData;
|
use MockAccountData;
|
||||||
|
|
||||||
|
protected $faker;
|
||||||
|
|
||||||
protected function setUp() :void
|
protected function setUp() :void
|
||||||
{
|
{
|
||||||
parent::setUp();
|
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()
|
public function testSetTaxId()
|
||||||
{
|
{
|
||||||
$p = Product::factory()->create([
|
$p = Product::factory()->create([
|
||||||
|
@ -397,6 +397,7 @@ class EventTest extends TestCase
|
|||||||
];
|
];
|
||||||
|
|
||||||
$quote = Quote::find($this->decodePrimaryKey($arr['data']['id']));
|
$quote = Quote::find($this->decodePrimaryKey($arr['data']['id']));
|
||||||
|
$quote->due_date = now()->addYear();
|
||||||
$quote->status_id = Quote::STATUS_SENT;
|
$quote->status_id = Quote::STATUS_SENT;
|
||||||
$quote->save();
|
$quote->save();
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Tests\Unit;
|
namespace Tests\Unit;
|
||||||
|
|
||||||
use App\Libraries\Currency\Conversion\CurrencyApi;
|
use App\Libraries\Currency\Conversion\CurrencyApi;
|
||||||
|
use App\Models\Currency;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
@ -26,6 +27,21 @@ class CurrencyApiTest extends TestCase
|
|||||||
parent::setUp();
|
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()
|
public function testCurrencyConversionWorking()
|
||||||
{
|
{
|
||||||
$converter = new CurrencyApi();
|
$converter = new CurrencyApi();
|
||||||
|
@ -11,23 +11,24 @@
|
|||||||
|
|
||||||
namespace Tests\Unit;
|
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\ClientFactory;
|
||||||
use App\Factory\VendorFactory;
|
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\Utils\Traits\MakesHash;
|
||||||
|
use App\Models\RecurringInvoice;
|
||||||
|
use App\DataMapper\ClientSettings;
|
||||||
|
use App\Utils\Traits\GeneratesCounter;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|
||||||
use Illuminate\Support\Facades\Session;
|
use Illuminate\Support\Facades\Session;
|
||||||
use Tests\MockAccountData;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
@ -53,6 +54,121 @@ class GeneratesCounterTest extends TestCase
|
|||||||
$this->makeTestData();
|
$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()
|
public function testResetCounter()
|
||||||
{
|
{
|
||||||
$timezone = Timezone::find(1);
|
$timezone = Timezone::find(1);
|
||||||
@ -82,8 +198,8 @@ class GeneratesCounterTest extends TestCase
|
|||||||
$this->company->settings = $settings;
|
$this->company->settings = $settings;
|
||||||
$this->company->save();
|
$this->company->save();
|
||||||
|
|
||||||
$this->client->settings = $settings;
|
// $this->client->settings = $settings;
|
||||||
$this->client->save();
|
// $this->client->save();
|
||||||
|
|
||||||
$this->travel(5)->days();
|
$this->travel(5)->days();
|
||||||
$date_formatted = now($timezone->name)->format('Ymd');
|
$date_formatted = now($timezone->name)->format('Ymd');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user