Merge pull request #11250 from turbo124/v5-develop

v5.12.23
This commit is contained in:
David Bomba 2025-09-02 07:07:01 +10:00 committed by GitHub
commit fd69659806
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
98 changed files with 13650 additions and 560 deletions

View File

@ -1 +1 @@
5.12.22
5.12.23

View File

@ -970,14 +970,14 @@ class CheckData extends Command
public function checkClientSettings()
{
if ($this->option('fix') == 'true') {
Client::query()->whereNull('country_id')->orWhere('country_id', 0)->cursor()->each(function ($client) {
Client::query()->withTrashed()->whereNull('country_id')->orWhere('country_id', 0)->cursor()->each(function ($client) {
$client->country_id = $client->company->settings->country_id;
$client->saveQuietly();
$this->logMessage("Fixing country for # {$client->id}");
});
Client::query()->whereNull("settings->currency_id")->orWhereJsonContains('settings', ['currency_id' => ''])->cursor()->each(function ($client) {
Client::query()->withTrashed()->whereNull("settings->currency_id")->orWhereJsonContains('settings', ['currency_id' => ''])->cursor()->each(function ($client) {
$settings = $client->settings;
$settings->currency_id = (string)$client->company->settings->currency_id;
$client->settings = $settings;
@ -987,7 +987,7 @@ class CheckData extends Command
});
Payment::withTrashed()->where('exchange_rate', 0)->cursor()->each(function ($payment) {
Payment::withTrashed()->withTrashed()->where('exchange_rate', 0)->cursor()->each(function ($payment) {
$payment->exchange_rate = 1;
$payment->saveQuietly();
@ -1000,11 +1000,11 @@ class CheckData extends Command
})
->cursor()
->each(function ($p) {
$p->currency_id = $p->client->settings->currency_id;
$p->currency_id = $p->client->settings->currency_id ?? $p->company->settings->currency_id;
$p->saveQuietly();
$this->logMessage("Fixing currency for # {$p->id}");
$this->logMessage("Fixing currency for # {$p->id} with new currency {$p->currency_id}");
});
@ -1015,7 +1015,7 @@ class CheckData extends Command
$c->subdomain = MultiDB::randomSubdomainGenerator();
$c->save();
$this->logMessage("Fixing subdomain for # {$c->id}");
$this->logMessage("Fixing subdomain for # {$c->id} with new subdomain {$c->subdomain}");
});
@ -1076,7 +1076,7 @@ class CheckData extends Command
public function checkVendorSettings()
{
if ($this->option('fix') == 'true') {
Vendor::query()->whereNull('currency_id')->orWhere('currency_id', '')->cursor()->each(function ($vendor) {
Vendor::query()->withTrashed()->whereNull('currency_id')->orWhere('currency_id', '')->cursor()->each(function ($vendor) {
$vendor->currency_id = $vendor->company->settings->currency_id;
$vendor->saveQuietly();
@ -1174,7 +1174,7 @@ class CheckData extends Command
$bt->invoice_ids = collect($bt->payment->invoices)->pluck('hashed_id')->implode(',');
$bt->save();
$this->logMessage("Fixing - {$bt->id}");
$this->logMessage("Fixing - {$bt->id} with new invoice IDs {$bt->invoice_ids}");
}
@ -1195,7 +1195,7 @@ class CheckData extends Command
$c->send_email = false;
$c->saveQuietly();
$this->logMessage("Fixing - {$c->id}");
$this->logMessage("Fixing - {$c->id} with new send email {$c->send_email}");
});
}
@ -1234,11 +1234,11 @@ class CheckData extends Command
if ($this->option('fix') == 'true') {
$p->cursor()->each(function ($c) {
$c->currency_id = $c->client->settings->currency_id ?? $c->company->settings->currency_id;
$c->saveQuietly();
$p->cursor()->each(function ($payment) {
$payment->currency_id = $payment->client->settings->currency_id ?? $payment->company->settings->currency_id;
$payment->saveQuietly();
$this->logMessage("Fixing - {$c->id}");
$this->logMessage("Fixing payment ID - {$payment->id} with new currency {$payment->currency_id}");
});
}

View File

@ -36,6 +36,7 @@ class TranslationsExport extends Command
protected $log = '';
private array $langs = [
'af_ZA',
'ar',
'bg',
'ca',
@ -56,6 +57,7 @@ class TranslationsExport extends Command
'he',
'hr',
'hu',
'id_ID',
'it',
'ja',
'km_KH',

View File

@ -534,8 +534,10 @@ class CompanySettings extends BaseSettings
public string $ses_access_key = '';
public string $ses_region = '';
public string $ses_topic_arn = '';
public string $ses_from_address = '';
public static $casts = [
'ses_from_address' => 'string',
'ses_topic_arn' => 'string',
'ses_secret_key' => 'string',
'ses_access_key' => 'string',

View File

@ -329,8 +329,6 @@ class LoginController extends BaseController
Auth::login($existing_login_user, false);
/** @var \App\Models\User $user */
// $user = auth()->user();
$existing_login_user->update([
'oauth_user_id' => $user->id,
'oauth_provider_id' => $provider,

View File

@ -19,6 +19,7 @@ use App\Models\Invoice;
use App\Models\Project;
use Elastic\Elasticsearch\ClientBuilder;
use App\Http\Requests\Search\GenericSearchRequest;
use Illuminate\Support\Str;
class SearchController extends Controller
{
@ -44,6 +45,8 @@ class SearchController extends Controller
private array $projects = [];
private array $tasks = [];
public function __invoke(GenericSearchRequest $request)
{
if (config('scout.driver') == 'elastic') {
@ -86,17 +89,42 @@ class SearchController extends Controller
$params = [
// 'index' => 'clients,invoices,client_contacts',
'index' => 'clients,invoices,client_contacts,quotes,expenses,credits,recurring_invoices,vendors,vendor_contacts,purchase_orders,projects',
'body' => [
// 'index' => 'clients,invoices,client_contacts,quotes,expenses,credits,recurring_invoices,vendors,vendor_contacts,purchase_orders,projects',
'index' => 'clients_v2,invoices_v2,client_contacts_v2,quotes_v2,expenses_v2,credits_v2,recurring_invoices_v2,vendors_v2,vendor_contacts_v2,purchase_orders_v2,projects_v2,tasks_v2',
'body' => [
'query' => [
'bool' => [
'must' => [
'multi_match' => [
'query' => $search,
'fields' => ['*'],
'fuzziness' => 'AUTO',
'should' => [
[
'multi_match' => [
'query' => $search,
'fields' => ['*'],
'fuzziness' => 'AUTO',
]
],
// Safe nested search that won't fail on missing fields
[
'nested' => [
'path' => 'line_items',
'query' => [
'multi_match' => [
'query' => $search,
'fields' => [
'line_items.product_key^2',
'line_items.notes^2',
'line_items.custom_value1',
'line_items.custom_value2',
'line_items.custom_value3',
'line_items.custom_value4'
],
'fuzziness' => 'AUTO',
]
],
'ignore_unmapped' => true
]
],
],
'minimum_should_match' => 1,
'filter' => [
'match' => [
'company_key' => $company->company_key,
@ -108,8 +136,11 @@ class SearchController extends Controller
],
];
$results = $elastic->search($params);
nlog($results['hits']);
$this->mapResults($results['hits']['hits'] ?? []);
return response()->json([
@ -124,6 +155,7 @@ class SearchController extends Controller
'vendor_contacts' => $this->vendor_contacts,
'purchase_orders' => $this->purchase_orders,
'projects' => $this->projects,
'tasks' => $this->tasks,
'settings' => $this->settingsMap(),
], 200);
@ -133,8 +165,8 @@ class SearchController extends Controller
{
foreach ($results as $result) {
switch ($result['_index']) {
case 'clients':
switch (true) {
case Str::startsWith($result['_index'], 'clients'):
if ($result['_source']['is_deleted']) { //do not return deleted results
break;
@ -148,7 +180,7 @@ class SearchController extends Controller
];
break;
case 'invoices':
case Str::startsWith($result['_index'], 'invoices'):
if ($result['_source']['is_deleted']) { //do not return deleted invoices
break;
@ -162,7 +194,7 @@ class SearchController extends Controller
'path' => "/invoices/{$result['_source']['hashed_id']}/edit"
];
break;
case 'client_contacts':
case Str::startsWith($result['_index'], 'client_contacts'):
if ($result['_source']['__soft_deleted']) {
break;
@ -175,7 +207,7 @@ class SearchController extends Controller
'path' => "/clients/{$result['_source']['client_id']}"
];
break;
case 'quotes':
case Str::startsWith($result['_index'], 'quotes'):
if ($result['_source']['__soft_deleted']) {
break;
@ -190,7 +222,7 @@ class SearchController extends Controller
break;
case 'expenses':
case Str::startsWith($result['_index'], 'expenses'):
if ($result['_source']['__soft_deleted']) {
break;
@ -205,7 +237,7 @@ class SearchController extends Controller
break;
case 'credits':
case Str::startsWith($result['_index'], 'credits'):
if ($result['_source']['__soft_deleted']) {
break;
@ -220,7 +252,7 @@ class SearchController extends Controller
break;
case 'recurring_invoices':
case Str::startsWith($result['_index'], 'recurring_invoices'):
if ($result['_source']['__soft_deleted']) {
break;
@ -235,7 +267,7 @@ class SearchController extends Controller
break;
case 'vendors':
case Str::startsWith($result['_index'], 'vendors'):
if ($result['_source']['__soft_deleted']) {
break;
@ -250,7 +282,7 @@ class SearchController extends Controller
break;
case 'vendor_contacts':
case Str::startsWith($result['_index'], 'vendor_contacts'):
if ($result['_source']['__soft_deleted']) {
break;
@ -265,7 +297,7 @@ class SearchController extends Controller
break;
case 'purchase_orders':
case Str::startsWith($result['_index'], 'purchase_orders'):
if ($result['_source']['__soft_deleted']) {
break;
@ -280,7 +312,7 @@ class SearchController extends Controller
break;
case 'projects':
case Str::startsWith($result['_index'], 'projects'):
if ($result['_source']['__soft_deleted']) {
break;
@ -293,6 +325,20 @@ class SearchController extends Controller
'path' => "/projects/{$result['_source']['hashed_id']}"
];
break;
case Str::startsWith($result['_index'], 'tasks'):
if ($result['_source']['is_deleted']) {
break;
}
$this->tasks[] = [
'name' => $result['_source']['name'],
'type' => '/task',
'id' => $result['_source']['hashed_id'],
'path' => "/tasks/{$result['_source']['hashed_id']}/edit"
];
break;
}
}

View File

@ -114,6 +114,7 @@ class UpdateCompanyRequest extends Request
$rules['settings.ses_secret_key'] = 'required_if:settings.email_sending_method,client_ses'; //ses specific rules
$rules['settings.ses_access_key'] = 'required_if:settings.email_sending_method,client_ses'; //ses specific rules
$rules['settings.ses_region'] = 'required_if:settings.email_sending_method,client_ses'; //ses specific rules
$rules['settings.ses_from_address'] = 'required_if:settings.email_sending_method,client_ses'; //ses specific rules
$rules['settings.reply_to_email'] = 'sometimes|nullable|email'; // ensures that the reply to email address is a valid email address
$rules['settings.bcc_email'] = ['sometimes', 'nullable', new \App\Rules\CommaSeparatedEmails]; //ensure that the BCC's are valid comma separated emails

View File

@ -22,6 +22,7 @@ class BlackListRule implements ValidationRule
{
/** Bad domains +/- disposable email domains */
private array $blacklist = [
"freedrops.org",
"mailshan.com",
"tabletship.com",
"tiktook.lol",

View File

@ -101,7 +101,7 @@ class TaskTransformer extends BaseTransformer
return '';
}
return [$start_date, $end_date, $notes, $is_billable];
return [(int)$start_date, (int)$end_date, (string)$notes, (bool)$is_billable];
}
private function resolveStartDate($item)
@ -117,11 +117,24 @@ class TaskTransformer extends BaseTransformer
return $stub_start_date->timestamp;
} catch (\Exception $e) {
nlog("fall back failed too" . $e->getMessage());
// return $this->stubbed_timestamp;
}
try {
$stub_start_date = \Carbon\Carbon::parse(str_replace('/', '-', $stub_start_date));
$this->stubbed_timestamp = $stub_start_date->timestamp;
return $stub_start_date->timestamp;
} catch (\Exception $e) {
nlog("str replace fall back failed too" . $e->getMessage());
}
try {
$stub_start_date = \Carbon\Carbon::createFromFormat($this->company->date_format(), $stub_start_date);
@ -145,7 +158,7 @@ class TaskTransformer extends BaseTransformer
$stub_end_date = \Carbon\Carbon::parse($stub_end_date);
if ($stub_end_date->timestamp == $this->stubbed_timestamp) {
$this->stubbed_timestamp;
return $this->stubbed_timestamp;
}
@ -159,6 +172,25 @@ class TaskTransformer extends BaseTransformer
try {
$stub_end_date = \Carbon\Carbon::parse(str_replace('/', '-', $stub_end_date));
if ($stub_end_date->timestamp == $this->stubbed_timestamp) {
return $this->stubbed_timestamp;
}
$this->stubbed_timestamp = $stub_end_date->timestamp;
return $stub_end_date->timestamp;
} catch (\Exception $e) {
nlog($e->getMessage());
// return $this->stubbed_timestamp;
}
try {
$stub_end_date = \Carbon\Carbon::createFromFormat($this->company->date_format(), $stub_end_date);

View File

@ -66,6 +66,8 @@ class NinjaMailerJob implements ShouldQueue
protected $client_brevo_secret = false;
protected $client_ses_secret = false;
public function __construct(public ?NinjaMailerObject $nmo, public bool $override = false)
{
}
@ -138,6 +140,10 @@ class NinjaMailerJob implements ShouldQueue
$mailer->brevo_config($this->client_brevo_secret);
}
if($this->client_ses_secret) {
$mailer->ses_config($this->nmo->settings->ses_access_key, $this->nmo->settings->ses_secret_key, $this->nmo->settings->ses_region, $this->nmo->settings->ses_topic_arn);
}
$mailable = $this->nmo->mailable;
/** May need to re-build it here @todo explain why we need this? */
@ -394,6 +400,15 @@ class NinjaMailerJob implements ShouldQueue
$this->mailer = 'mailgun';
$this->setHostedMailgunMailer();
return $this;
case 'ses':
$this->mailer = 'ses';
$this->setHostedSesMailer();
return $this;
case 'client_ses':
$this->mailer = 'ses';
$this->client_ses_secret = true;
$this->setSesMailer();
return $this;
case 'gmail':
$this->mailer = 'gmail';
$this->setGmailMailer();
@ -430,6 +445,22 @@ class NinjaMailerJob implements ShouldQueue
return $this;
}
private function setHostedSesMailer()
{
if (property_exists($this->nmo->settings, 'email_from_name') && strlen($this->nmo->settings->email_from_name) > 1) {
$email_from_name = $this->nmo->settings->email_from_name;
} else {
$email_from_name = $this->company->present()->name();
}
$this->nmo
->mailable
->from(config('services.ses.from.address'), $email_from_name);
}
private function configureSmtpMailer()
{
@ -521,6 +552,8 @@ class NinjaMailerJob implements ShouldQueue
$this->client_brevo_secret = false;
$this->client_ses_secret = false;
//always dump the drivers to prevent reuse
app('mail.manager')->forgetMailers();
}
@ -622,6 +655,20 @@ class NinjaMailerJob implements ShouldQueue
->from($sending_email, $sending_user);
}
private function setSesMailer(): self
{
$this->mailer = 'ses';
$user = $this->resolveSendingUser();
$sending_user = (isset($this->nmo->settings->email_from_name) && strlen($this->nmo->settings->email_from_name) > 2) ? $this->nmo->settings->email_from_name : $user->name();
$this->nmo
->mailable
->from($this->nmo->settings->ses_from_address, $sending_user);
return $this;
}
/**
* Configures Postmark using client supplied secret
* as the Mailer
@ -764,7 +811,7 @@ class NinjaMailerJob implements ShouldQueue
}
/* GMail users are uncapped */
if (Ninja::isHosted() && (in_array($this->nmo->settings->email_sending_method, ['gmail', 'office365', 'client_postmark', 'client_mailgun', 'client_brevo']))) {
if (Ninja::isHosted() && (in_array($this->nmo->settings->email_sending_method, ['gmail', 'office365', 'client_postmark', 'client_mailgun', 'client_brevo', 'client_ses']))) {
return false;
}

View File

@ -321,7 +321,6 @@ class SendReminders implements ShouldQueue
$invoice->client->service()->calculateBalance();
// $invoice->client->service()->updateBalance($invoice->balance - $temp_invoice_balance)->save();
$invoice->ledger()->updateInvoiceBalance($invoice->balance - $temp_invoice_balance, "Late Fee Adjustment for invoice {$invoice->number}");
return $invoice;

View File

@ -26,6 +26,8 @@ class PaymentBalanceActivity implements ShouldQueue
public $delay = 5;
public $deleteWhenMissingModels = true;
/**
* Create the event listener.
*

View File

@ -133,6 +133,16 @@ class Client extends BaseModel implements HasLocalePreference
use Excludable;
use Searchable;
/**
* Get the index name for the model.
*
* @return string
*/
public function searchableAs(): string
{
return 'clients_v2';
}
protected $presenter = ClientPresenter::class;
protected $hidden = [
@ -257,9 +267,9 @@ class Client extends BaseModel implements HasLocalePreference
return [
'id' => $this->company->db.":".$this->id,
'name' => $name,
'is_deleted' => $this->is_deleted,
'is_deleted' => (bool)$this->is_deleted,
'hashed_id' => $this->hashed_id,
'number' => $this->number,
'number' => (string)$this->number,
'id_number' => $this->id_number,
'vat_number' => $this->vat_number,
'balance' => $this->balance,

View File

@ -169,6 +169,11 @@ class ClientContact extends Authenticatable implements HasLocalePreference
'email',
];
public function searchableAs(): string
{
return 'client_contacts_v2';
}
public function toSearchableArray()
{
return [

View File

@ -143,6 +143,16 @@ class Credit extends BaseModel
use MakesReminders;
use Searchable;
/**
* Get the index name for the model.
*
* @return string
*/
public function searchableAs(): string
{
return 'credits_v2';
}
protected $presenter = CreditPresenter::class;
protected $fillable = [
@ -213,8 +223,8 @@ class Credit extends BaseModel
'id' => $this->company->db.":".$this->id,
'name' => ctrans('texts.credit') . " " . $this->number . " | " . $this->client->present()->name() . ' | ' . Number::formatMoney($this->amount, $this->company) . ' | ' . $this->translateDate($this->date, $this->company->date_format(), $locale),
'hashed_id' => $this->hashed_id,
'number' => $this->number,
'is_deleted' => $this->is_deleted,
'number' => (string)$this->number,
'is_deleted' => (bool)$this->is_deleted,
'amount' => (float) $this->amount,
'balance' => (float) $this->balance,
'due_date' => $this->due_date,

View File

@ -102,6 +102,16 @@ class Expense extends BaseModel
use Filterable;
use Searchable;
/**
* Get the index name for the model.
*
* @return string
*/
public function searchableAs(): string
{
return 'expenses_v2';
}
protected $fillable = [
'client_id',
'assigned_user_id',
@ -177,8 +187,8 @@ class Expense extends BaseModel
'id' => $this->company->db.":".$this->id,
'name' => ctrans('texts.expense') . " " . ($this->number ?? '') . ' | ' . Number::formatMoney($this->amount, $this->company) . ' | ' . $this->translateDate($this->date, $this->company->date_format(), $locale),
'hashed_id' => $this->hashed_id,
'number' => $this->number,
'is_deleted' => $this->is_deleted,
'number' => (string)$this->number,
'is_deleted' => (bool)$this->is_deleted,
'amount' => (float) $this->amount,
'date' => $this->date ?? null,
'custom_value1' => (string)$this->custom_value1,
@ -186,6 +196,8 @@ class Expense extends BaseModel
'custom_value3' => (string)$this->custom_value3,
'custom_value4' => (string)$this->custom_value4,
'company_key' => $this->company->company_key,
'public_notes' => (string)$this->public_notes,
'private_notes' => (string)$this->private_notes
];
}

View File

@ -255,6 +255,11 @@ class Invoice extends BaseModel
// return 'invoices_index'; // for when we need to rename
// }
public function searchableAs(): string
{
return 'invoices_v2';
}
public function toSearchableArray()
{
$locale = $this->company->locale();
@ -264,8 +269,8 @@ class Invoice extends BaseModel
'id' => (string)$this->company->db.":".$this->id,
'name' => ctrans('texts.invoice') . " " . $this->number . " | " . $this->client->present()->name() . ' | ' . Number::formatMoney($this->amount, $this->company) . ' | ' . $this->translateDate($this->date, $this->company->date_format(), $locale),
'hashed_id' => $this->hashed_id,
'number' => $this->number,
'is_deleted' => $this->is_deleted,
'number' => (string)$this->number,
'is_deleted' => (bool)$this->is_deleted,
'amount' => (float) $this->amount,
'balance' => (float) $this->balance,
'due_date' => $this->due_date,
@ -276,7 +281,7 @@ class Invoice extends BaseModel
'custom_value4' => (string)$this->custom_value4,
'company_key' => $this->company->company_key,
'po_number' => (string)$this->po_number,
// 'line_items' => $this->line_items,
'line_items' => (array)$this->line_items,
];
}

View File

@ -24,22 +24,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property int $user_id
* @property int|null $vendor_id
* @property int|null $client_id
* @property int|null $assigned_user_id
* @property string|null $name
* @property string|null $website
* @property string|null $private_notes
* @property string|null $public_notes
* @property string|null $client_hash
* @property string|null $logo
* @property string|null $phone
* @property string|null $routing_id
* @property float $balance
* @property float $paid_to_date
* @property float $credit_balance
* @property int|null $last_login
* @property int|null $industry_id
* @property int|null $size_id
* @property object|array|null $e_invoice
* @property string|null $address1
* @property string|null $address2
* @property string|null $city
@ -52,10 +37,10 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property string|null $custom_value4
* @property bool $is_deleted
* @property bool $is_shipping_location
* @property object|array|null $tax_data
* @property int|null $created_at
* @property int|null $updated_at
* @property int|null $deleted_at
* @property object|array|null $tax_data
* @property-read mixed $hashed_id
* @property-read \App\Models\User $user
* @property-read \App\Models\Client|null $client

View File

@ -69,6 +69,16 @@ class Project extends BaseModel
use Filterable;
use Searchable;
/**
* Get the index name for the model.
*
* @return string
*/
public function searchableAs(): string
{
return 'projects_v2';
}
protected $fillable = [
'name',
'client_id',
@ -106,8 +116,8 @@ class Project extends BaseModel
'id' => (string)$this->company->db.":".$this->id,
'name' => ctrans('texts.project') . " " . $this->number . ' | ' . $this->name . " | " . $this->client->present()->name(),
'hashed_id' => $this->hashed_id,
'number' => $this->number,
'is_deleted' => $this->is_deleted,
'number' => (string)$this->number,
'is_deleted' => (bool)$this->is_deleted,
'task_rate' => (float) $this->task_rate,
'budgeted_hours' => (float) $this->budgeted_hours,
'due_date' => $this->due_date,

View File

@ -129,6 +129,16 @@ class PurchaseOrder extends BaseModel
use SoftDeletes;
use Searchable;
/**
* Get the index name for the model.
*
* @return string
*/
public function searchableAs(): string
{
return 'purchase_orders_v2';
}
protected $hidden = [
'id',
'private_notes',
@ -218,8 +228,8 @@ class PurchaseOrder extends BaseModel
'id' => $this->company->db.":".$this->id,
'name' => ctrans('texts.purchase_order') . " " . $this->number . " | " . $this->vendor->present()->name() . ' | ' . Number::formatMoney($this->amount, $this->company) . ' | ' . $this->translateDate($this->date, $this->company->date_format(), $locale),
'hashed_id' => $this->hashed_id,
'number' => $this->number,
'is_deleted' => $this->is_deleted,
'number' => (string)$this->number,
'is_deleted' => (bool)$this->is_deleted,
'amount' => (float) $this->amount,
'balance' => (float) $this->balance,
'due_date' => $this->due_date,

View File

@ -131,6 +131,16 @@ class Quote extends BaseModel
use MakesInvoiceValues;
use Searchable;
/**
* Get the index name for the model.
*
* @return string
*/
public function searchableAs(): string
{
return 'quotes_v2';
}
protected $presenter = QuotePresenter::class;
protected $touches = [];
@ -210,8 +220,8 @@ class Quote extends BaseModel
'id' => $this->company->db.":".$this->id,
'name' => ctrans('texts.quote') . " " . ($this->number ?? '') . " | " . $this->client->present()->name() . ' | ' . Number::formatMoney($this->amount, $this->company) . ' | ' . $this->translateDate($this->date, $this->company->date_format(), $locale),
'hashed_id' => $this->hashed_id,
'number' => $this->number,
'is_deleted' => $this->is_deleted,
'number' => (string)$this->number,
'is_deleted' => (bool)$this->is_deleted,
'amount' => (float) $this->amount,
'balance' => (float) $this->balance,
'due_date' => $this->due_date,

View File

@ -138,6 +138,7 @@ class RecurringInvoice extends BaseModel
use PresentableTrait;
use Searchable;
protected $presenter = RecurringInvoicePresenter::class;
/**
@ -270,17 +271,65 @@ class RecurringInvoice extends BaseModel
'remaining_cycles',
];
/**
* Get the index name for the model.
*
* @return string
*/
public function searchableAs(): string
{
return 'recurring_invoices_v2';
}
public function toSearchableArray()
{
$locale = $this->company->locale();
App::setLocale($locale);
// Properly cast line items to ensure correct types
$line_items = [];
if ($this->line_items) {
foreach ($this->line_items as $item) {
$line_items[] = [
'quantity' => (float)($item->quantity ?? 0),
'net_cost' => (float)($item->net_cost ?? 0),
'cost' => (float)($item->cost ?? 0),
'product_key' => (string)($item->product_key ?? ''),
'product_cost' => (float)($item->product_cost ?? 0),
'notes' => (string)($item->notes ?? ''),
'discount' => (float)($item->discount ?? 0),
'is_amount_discount' => (bool)($item->is_amount_discount ?? false),
'tax_name1' => (string)($item->tax_name1 ?? ''),
'tax_rate1' => (float)($item->tax_rate1 ?? 0),
'tax_name2' => (string)($item->tax_name2 ?? ''),
'tax_rate2' => (float)($item->tax_rate2 ?? 0),
'tax_name3' => (string)($item->tax_name3 ?? ''),
'tax_rate3' => (float)($item->tax_rate3 ?? 0),
'sort_id' => (string)($item->sort_id ?? ''),
'line_total' => (float)($item->line_total ?? 0),
'gross_line_total' => (float)($item->gross_line_total ?? 0),
'tax_amount' => (float)($item->tax_amount ?? 0),
'date' => (string)($item->date ?? ''),
'custom_value1' => (string)($item->custom_value1 ?? ''),
'custom_value2' => (string)($item->custom_value2 ?? ''),
'custom_value3' => (string)($item->custom_value3 ?? ''),
'custom_value4' => (string)($item->custom_value4 ?? ''),
'type_id' => (string)($item->type_id ?? ''),
'tax_id' => (string)($item->tax_id ?? ''),
'task_id' => (string)($item->task_id ?? ''),
'expense_id' => (string)($item->expense_id ?? ''),
'unit_code' => (string)($item->unit_code ?? ''),
];
}
}
return [
'id' => $this->company->db.":".$this->id,
'name' => ctrans('texts.recurring_invoice') . " " . $this->number . " | " . $this->client->present()->name() . ' | ' . Number::formatMoney($this->amount, $this->company) . ' | ' . $this->translateDate($this->date, $this->company->date_format(), $locale),
'hashed_id' => $this->hashed_id,
'number' => $this->number,
'is_deleted' => $this->is_deleted,
'number' => (string)$this->number,
'is_deleted' => (bool)$this->is_deleted,
'amount' => (float) $this->amount,
'balance' => (float) $this->balance,
'due_date' => $this->due_date,
@ -291,6 +340,7 @@ class RecurringInvoice extends BaseModel
'custom_value4' => (string)$this->custom_value4,
'company_key' => $this->company->company_key,
'po_number' => (string)$this->po_number,
'line_items' => $line_items,
];
}

View File

@ -16,6 +16,8 @@ use Carbon\CarbonInterval;
use App\Models\CompanyUser;
use Illuminate\Support\Carbon;
use App\Utils\Traits\MakesHash;
use Illuminate\Support\Facades\App;
use Elastic\ScoutDriverPlus\Searchable;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Libraries\Currency\Conversion\CurrencyApi;
@ -105,6 +107,17 @@ class Task extends BaseModel
use MakesHash;
use SoftDeletes;
use Filterable;
use Searchable;
/**
* Get the index name for the model.
*
* @return string
*/
public function searchableAs(): string
{
return 'tasks_v2';
}
protected $fillable = [
'client_id',
@ -147,6 +160,79 @@ class Task extends BaseModel
return self::class;
}
public function toSearchableArray()
{
$locale = $this->company->locale();
App::setLocale($locale);
$project = $this->project ? " | [ {$this->project->name} ]" : ' ';
$client = $this->client ? " | {$this->client->present()->name()} ]" : ' ';
// Get basic data
$data = [
'id' => $this->company->db.":".$this->id,
'name' => ctrans('texts.task') . " " . ($this->number ?? '') . $project . $client,
'hashed_id' => $this->hashed_id,
'number' => (string)$this->number,
'description' => (string)$this->description,
'task_rate' => (float) $this->rate,
'is_deleted' => (bool) $this->is_deleted,
'custom_value1' => (string) $this->custom_value1,
'custom_value2' => (string) $this->custom_value2,
'custom_value3' => (string) $this->custom_value3,
'custom_value4' => (string) $this->custom_value4,
'company_key' => $this->company->company_key,
'time_log' => $this->normalizeTimeLog($this->time_log),
'calculated_start_date' => (string) $this->calculated_start_date,
];
return $data;
}
/**
* Normalize time_log for Elasticsearch indexing
* Handles polymorphic structure: [start, end?, description?, billable?]
*/
private function normalizeTimeLog($time_log): array
{
// Handle null/empty cases
if (empty($time_log)) {
return [];
}
$logs = json_decode($time_log, true);
// Validate decoded data
if (!is_array($logs) || empty($logs)) {
return [];
}
$normalized = [];
foreach ($logs as $log) {
// Skip invalid entries
if (!is_array($log) || !isset($log[0])) {
continue;
}
$normalized[] = [
'start_time' => (int) $log[0],
'end_time' => isset($log[1]) && $log[1] !== 0 ? (int) $log[1] : 0,
'description' => isset($log[2]) ? trim((string) $log[2]) : '',
'billable' => isset($log[3]) ? (bool) $log[3] : false,
'is_running' => isset($log[1]) && $log[1] === 0,
];
}
return $normalized;
}
public function getScoutKey()
{
return $this->company->db.":".$this->id;
}
/**
* Get all of the users that belong to the team.
*

View File

@ -101,6 +101,16 @@ class Vendor extends BaseModel
use AppSetup;
use Searchable;
/**
* Get the index name for the model.
*
* @return string
*/
public function searchableAs(): string
{
return 'vendors_v2';
}
protected $fillable = [
'name',
'assigned_user_id',
@ -161,9 +171,9 @@ class Vendor extends BaseModel
return [
'id' => $this->company->db.":".$this->id,
'name' => $name,
'is_deleted' => $this->is_deleted,
'is_deleted' => (bool)$this->is_deleted,
'hashed_id' => $this->hashed_id,
'number' => $this->number,
'number' => (string)$this->number,
'id_number' => $this->id_number,
'vat_number' => $this->vat_number,
'phone' => $this->phone,

View File

@ -126,20 +126,25 @@ class VendorContact extends Authenticatable implements HasLocalePreference
'send_email',
];
public function searchableAs(): string
{
return 'vendor_contacts_v2';
}
public function toSearchableArray()
{
return [
'id' => $this->company->db.":".$this->id,
'name' => $this->present()->search_display(),
'hashed_id' => $this->hashed_id,
'email' => $this->email,
'first_name' => $this->first_name,
'last_name' => $this->last_name,
'phone' => $this->phone,
'custom_value1' => $this->custom_value1,
'custom_value2' => $this->custom_value2,
'custom_value3' => $this->custom_value3,
'custom_value4' => $this->custom_value4,
'email' => (string)$this->email,
'first_name' => (string)$this->first_name,
'last_name' => (string)$this->last_name,
'phone' => (string)$this->phone,
'custom_value1' => (string)$this->custom_value1,
'custom_value2' => (string)$this->custom_value2,
'custom_value3' => (string)$this->custom_value3,
'custom_value4' => (string)$this->custom_value4,
'company_key' => $this->company->company_key,
'vendor_id' => $this->vendor->hashed_id,
];

View File

@ -121,6 +121,7 @@ class AppServiceProvider extends ServiceProvider
return $this;
});
Mail::extend('brevo', function () {
return (new BrevoTransportFactory())->create(
new Dsn(
@ -145,6 +146,25 @@ class AppServiceProvider extends ServiceProvider
return $this;
});
// Macro to configure SES with runtime credentials
Mailer::macro('ses_config', function (string $key, string $secret, string $region = 'us-east-1', ?string $topic_arn = null) {
$config = [
'transport' => 'ses',
'key' => $key,
'secret' => $secret,
'region' => $region,
];
if ($topic_arn) {
$config['configuration_set'] = $topic_arn;
}
// @phpstan-ignore /** @phpstan-ignore-next-line **/
Mailer::setSymfonyTransport(app('mail.manager')->createSymfonyTransport($config));
return $this;
});
//Prevents destructive commands from being run in hosted environments
\DB::prohibitDestructiveCommands(Ninja::isHosted());

View File

@ -0,0 +1,38 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Laravel\Scout\ScoutServiceProvider as BaseScoutServiceProvider;
class ScoutServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
// Only register Scout if driver is not null
if (config('scout.driver') !== 'null') {
$this->app->register(BaseScoutServiceProvider::class);
}
}
/**
* Bootstrap services.
*/
public function boot(): void
{
// Scout will be booted automatically by the base provider if driver is not null
}
}

View File

@ -78,6 +78,9 @@ class Email implements ShouldQueue
/** Brevo endpoint */
protected ?string $client_brevo_secret = null;
/** SES api key */
protected ?string $client_ses_secret = null;
/** Default mailer */
private string $mailer = 'default';
@ -263,11 +266,9 @@ class Email implements ShouldQueue
*/
public function email()
{
/* Init the mailer*/
$mailer = Mail::mailer($this->mailer);
/* Additional configuration if using a client third party mailer */
if ($this->client_postmark_secret) {
$mailer->postmark_config($this->client_postmark_secret);
@ -281,6 +282,10 @@ class Email implements ShouldQueue
$mailer->brevo_config($this->client_brevo_secret);
}
if ($this->client_ses_secret) {
$mailer->ses_config($this->email_object->settings->ses_access_key, $this->email_object->settings->ses_secret_key, $this->email_object->settings->ses_region, $this->email_object->settings->ses_topic_arn);
}
/* Attempt the send! */
try {
nlog("Using mailer => " . $this->mailer . " " . now()->toDateTimeString());
@ -463,7 +468,7 @@ class Email implements ShouldQueue
}
/* GMail users are uncapped */
if (in_array($this->email_object->settings->email_sending_method, ['gmail', 'office365', 'client_postmark', 'client_mailgun', 'client_brevo'])) {
if (in_array($this->email_object->settings->email_sending_method, ['gmail', 'office365', 'client_postmark', 'client_mailgun', 'client_brevo', 'client_ses'])) {
return false;
}
@ -532,6 +537,20 @@ class Email implements ShouldQueue
return false;
}
private function setHostedSesMailer()
{
if (property_exists($this->email_object->settings, 'email_from_name') && strlen($this->email_object->settings->email_from_name) > 1) {
$email_from_name = $this->email_object->settings->email_from_name;
} else {
$email_from_name = $this->company->present()->name();
}
$this->mailable
->from(config('services.ses.from.address'), $email_from_name);
}
private function setHostedMailgunMailer()
{
@ -601,6 +620,10 @@ class Email implements ShouldQueue
$this->mailer = 'mailgun';
$this->setHostedMailgunMailer();
return $this;
case 'ses':
$this->mailer = 'ses';
$this->setHostedSesMailer();
return $this;
case 'gmail':
$this->mailer = 'gmail';
$this->setGmailMailer();
@ -622,6 +645,10 @@ class Email implements ShouldQueue
$this->mailer = 'brevo';
$this->setBrevoMailer();
return $this;
case 'client_ses':
$this->mailer = 'ses';
$this->setSesMailer();
return $this;
case 'smtp':
$this->mailer = 'smtp';
$this->configureSmtpMailer();
@ -700,6 +727,8 @@ class Email implements ShouldQueue
$this->client_brevo_secret = null;
$this->client_ses_secret = null;
//always dump the drivers to prevent reuse
app('mail.manager')->forgetMailers();
}
@ -764,6 +793,21 @@ class Email implements ShouldQueue
$this->mailable
->from($sending_email, $sending_user);
}
private function setSesMailer()
{
$this->client_ses_secret = 'true';
$user = $this->resolveSendingUser();
$sending_user = (isset($this->email_object->settings->email_from_name) && strlen($this->email_object->settings->email_from_name) > 2) ? $this->email_object->settings->email_from_name : $user->name();
$this->mailable
->from($this->email_object->settings->ses_from_address, $sending_user);
}
/**
* Configures Brevo using client supplied secret
* as the Mailer

View File

@ -198,6 +198,15 @@ class HtmlEngine
$data['$payment_schedule_interval'] = ['value' => $this->entity->paymentScheduleInterval(), 'label' => ctrans('texts.payment_schedule')];
}
$data['$location1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'location1', $this->entity->location?->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'location1')];
$data['$location2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'location2', $this->entity->location?->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'location2')];
$data['$location3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'location3', $this->entity->location?->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'location3')];
$data['$location4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'location4', $this->entity->location?->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'location4')];
$data['$location.custom1'] = &$data['$location1'];
$data['$location.custom2'] = &$data['$location2'];
$data['$location.custom3'] = &$data['$location3'];
$data['$location.custom4'] = &$data['$location4'];
if ($this->entity_string == 'invoice' || $this->entity_string == 'recurring_invoice') {
$data['$entity'] = ['value' => ctrans('texts.invoice'), 'label' => ctrans('texts.invoice')];
$data['$number'] = ['value' => $this->entity->number ?: ' ', 'label' => ctrans('texts.invoice_number')];
@ -217,7 +226,7 @@ class HtmlEngine
$data['$invoice.custom2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice2', $this->entity->custom_value2, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice2')];
$data['$invoice.custom3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice3', $this->entity->custom_value3, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice3')];
$data['$invoice.custom4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'invoice4', $this->entity->custom_value4, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'invoice4')];
$data['$custom1'] = &$data['$invoice.custom1'];
$data['$custom2'] = &$data['$invoice.custom2'];
$data['$custom3'] = &$data['$invoice.custom3'];

View File

@ -41,16 +41,16 @@ class PDF extends FPDI
if ($this->text_alignment == 'L') {
$this->SetX($this->GetX() + $base_x);
// Adjust cell width to account for X offset
$cell_width = $this->GetPageWidth();
$cell_width = $this->GetPageWidth() + $base_x;
$this->Cell($cell_width, 5, $trans, 0, 0, 'L');
} elseif ($this->text_alignment == 'R') {
$this->SetX($this->GetX() + $base_x);
// For right alignment, calculate width from X position to right edge
$cell_width = $this->GetPageWidth();
$cell_width = $this->GetPageWidth() + $base_x;
$this->Cell($cell_width, 5, $trans, 0, 0, 'R');
} else {
// For center alignment, calculate appropriate width
$cell_width = $this->GetPageWidth();
$cell_width = $this->GetPageWidth() + $base_x;
$this->Cell($cell_width, 5, $trans, 0, 0, 'C');
}
}

View File

@ -179,6 +179,15 @@ class VendorHtmlEngine
$data['$subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getSubTotal(), $this->vendor) ?: '&nbsp;', 'label' => ctrans('texts.subtotal')];
$data['$gross_subtotal'] = ['value' => Number::formatMoney($this->entity_calc->getGrossSubTotal(), $this->vendor) ?: '&nbsp;', 'label' => ctrans('texts.subtotal')];
$data['$location1'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'location1', $this->entity->location?->custom_value1, $this->vendor) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'location1')];
$data['$location2'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'location2', $this->entity->location?->custom_value2, $this->vendor) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'location2')];
$data['$location3'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'location3', $this->entity->location?->custom_value3, $this->vendor) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'location3')];
$data['$location4'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'location4', $this->entity->location?->custom_value4, $this->vendor) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'location4')];
$data['$location.custom1'] = &$data['$location1'];
$data['$location.custom2'] = &$data['$location2'];
$data['$location.custom3'] = &$data['$location3'];
$data['$location.custom4'] = &$data['$location4'];
if ($this->entity->uses_inclusive_taxes) {
$data['$net_subtotal'] = ['value' => Number::formatMoney(($this->entity_calc->getSubTotal() - $this->entity->total_taxes - $this->entity_calc->getTotalDiscount()), $this->vendor) ?: '&nbsp;', 'label' => ctrans('texts.net_subtotal')];
} else {

View File

@ -50,7 +50,6 @@
"braintree/braintree_php": "^6.23",
"btcpayserver/btcpayserver-greenfield-php": "^2.6",
"checkout/checkout-sdk-php": "^3.0",
"doctrine/dbal": "^4.0",
"eway/eway-rapid-php": "^1.3",
"fakerphp/faker": "^1.14",
"getbrevo/brevo-php": "^1.0",
@ -60,15 +59,14 @@
"halaxa/json-machine": "^0.7.0",
"hashids/hashids": "^4.0",
"hedii/laravel-gelf-logger": "^9",
"horstoeko/orderx": "dev-master",
"horstoeko/orderx": "^1",
"horstoeko/zugferd": "^1",
"horstoeko/zugferdvisualizer": "^1",
"hyvor/php-json-exporter": "^0.0.3",
"imdhemy/laravel-purchases": "^1.7",
"intervention/image": "^2.5",
"invoiceninja/einvoice": "dev-main",
"invoiceninja/inspector": "^3.0",
"invoiceninja/ubl_invoice": "^2",
"invoiceninja/ubl_invoice": "^3",
"josemmo/facturae-php": "^1.7",
"laracasts/presenter": "^0.2.1",
"laravel/framework": "^v11",
@ -110,7 +108,7 @@
"symfony/mailer": "7.1.6",
"symfony/mailgun-mailer": "^7.1",
"symfony/postmark-mailer": "^7.1",
"turbo124/beacon": "^2",
"turbo124/beacon": "^3",
"twig/extra-bundle": "^3.18",
"twig/intl-extra": "^3.7",
"twig/markdown-extra": "^3.18",
@ -214,10 +212,6 @@
"type": "vcs",
"url": "https://github.com/invoiceninja/einvoice"
},
{
"type": "vcs",
"url": "https://github.com/turbo124/orderx"
},
{
"type": "vcs",
"url": "https://github.com/beganovich/php-ansible"
@ -233,4 +227,4 @@
],
"minimum-stability": "dev",
"prefer-stable": true
}
}

550
composer.lock generated
View File

@ -4,48 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "4711ddda232f939767da528b1a85215d",
"content-hash": "8318e1e4c83e423108e0155e52e26fd8",
"packages": [
{
"name": "adrienrn/php-mimetyper",
"version": "0.2.2",
"source": {
"type": "git",
"url": "https://github.com/adrienrn/php-mimetyper.git",
"reference": "702e00a604b4baed34d69730ce055e05c0f43932"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/adrienrn/php-mimetyper/zipball/702e00a604b4baed34d69730ce055e05c0f43932",
"reference": "702e00a604b4baed34d69730ce055e05c0f43932",
"shasum": ""
},
"require": {
"dflydev/apache-mime-types": "^1.0"
},
"type": "library",
"autoload": {
"psr-4": {
"MimeTyper\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Hussard",
"email": "adrien.ricartnoblet@gmail.com"
}
],
"description": "PHP mime type and extension mapping library: compatible with Symfony, powered by jshttp/mime-db",
"support": {
"issues": "https://github.com/adrienrn/php-mimetyper/issues",
"source": "https://github.com/adrienrn/php-mimetyper/tree/0.2.2"
},
"time": "2018-09-27T09:45:05+00:00"
},
{
"name": "afosto/yaac",
"version": "v1.5.3",
@ -1625,6 +1585,69 @@
],
"time": "2024-11-12T16:29:46+00:00"
},
{
"name": "dallgoot/yaml",
"version": "0.9.1.2",
"source": {
"type": "git",
"url": "https://github.com/dallgoot/yaml.git",
"reference": "99385863e0ffec5639f24844b616afe7cce6e14c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dallgoot/yaml/zipball/99385863e0ffec5639f24844b616afe7cce6e14c",
"reference": "99385863e0ffec5639f24844b616afe7cce6e14c",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-pcre": "*",
"ext-spl": "*",
"lib-pcre": "*",
"php": ">=8.1.14"
},
"require-dev": {
"ext-reflection": "*",
"phan/phan": "*",
"phpmetrics/phpmetrics": "*",
"phpunit/phpunit": "*"
},
"type": "library",
"autoload": {
"psr-4": {
"Dallgoot\\Yaml\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "dallgoot",
"email": "stephane.rebai@gmail.com"
}
],
"description": "Provides loader, dumper and an API for YAML content. Loader builds to equivalent data types in PHP 8.x",
"homepage": "https://github.com/dallgoot/yaml",
"keywords": [
"parser",
"reader",
"writer",
"yaml"
],
"support": {
"issues": "https://github.com/dallgoot/yaml/issues",
"source": "https://github.com/dallgoot/yaml/tree/0.9.1.2"
},
"funding": [
{
"url": "https://github.com/dallgoot",
"type": "github"
}
],
"time": "2023-12-30T01:42:24+00:00"
},
{
"name": "dasprid/enum",
"version": "1.0.6",
@ -1675,65 +1698,6 @@
},
"time": "2024-08-09T14:30:48+00:00"
},
{
"name": "dflydev/apache-mime-types",
"version": "v1.0.1",
"source": {
"type": "git",
"url": "https://github.com/dflydev/dflydev-apache-mime-types.git",
"reference": "f30a57e59b7476e4c5270b6a0727d79c9c0eb861"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dflydev/dflydev-apache-mime-types/zipball/f30a57e59b7476e4c5270b6a0727d79c9c0eb861",
"reference": "f30a57e59b7476e4c5270b6a0727d79c9c0eb861",
"shasum": ""
},
"require": {
"php": ">=5.3"
},
"require-dev": {
"twig/twig": "1.*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-0": {
"Dflydev\\ApacheMimeTypes": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Dragonfly Development Inc.",
"email": "info@dflydev.com",
"homepage": "http://dflydev.com"
},
{
"name": "Beau Simensen",
"email": "beau@dflydev.com",
"homepage": "http://beausimensen.com"
}
],
"description": "Apache MIME Types",
"keywords": [
"apache",
"mime",
"mimetypes"
],
"support": {
"issues": "https://github.com/dflydev/dflydev-apache-mime-types/issues",
"source": "https://github.com/dflydev/dflydev-apache-mime-types/tree/v1.0.1"
},
"time": "2013-05-14T02:02:01+00:00"
},
{
"name": "dflydev/dot-access-data",
"version": "v3.0.3",
@ -1809,112 +1773,6 @@
},
"time": "2024-07-08T12:26:09+00:00"
},
{
"name": "doctrine/dbal",
"version": "4.3.2",
"source": {
"type": "git",
"url": "https://github.com/doctrine/dbal.git",
"reference": "7669f131d43b880de168b2d2df9687d152d6c762"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/7669f131d43b880de168b2d2df9687d152d6c762",
"reference": "7669f131d43b880de168b2d2df9687d152d6c762",
"shasum": ""
},
"require": {
"doctrine/deprecations": "^1.1.5",
"php": "^8.2",
"psr/cache": "^1|^2|^3",
"psr/log": "^1|^2|^3"
},
"require-dev": {
"doctrine/coding-standard": "13.0.0",
"fig/log-test": "^1",
"jetbrains/phpstorm-stubs": "2023.2",
"phpstan/phpstan": "2.1.17",
"phpstan/phpstan-phpunit": "2.0.6",
"phpstan/phpstan-strict-rules": "^2",
"phpunit/phpunit": "11.5.23",
"slevomat/coding-standard": "8.16.2",
"squizlabs/php_codesniffer": "3.13.1",
"symfony/cache": "^6.3.8|^7.0",
"symfony/console": "^5.4|^6.3|^7.0"
},
"suggest": {
"symfony/console": "For helpful console commands such as SQL execution and import of files."
},
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\DBAL\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
}
],
"description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.",
"homepage": "https://www.doctrine-project.org/projects/dbal.html",
"keywords": [
"abstraction",
"database",
"db2",
"dbal",
"mariadb",
"mssql",
"mysql",
"oci8",
"oracle",
"pdo",
"pgsql",
"postgresql",
"queryobject",
"sasql",
"sql",
"sqlite",
"sqlserver",
"sqlsrv"
],
"support": {
"issues": "https://github.com/doctrine/dbal/issues",
"source": "https://github.com/doctrine/dbal/tree/4.3.2"
},
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal",
"type": "tidelift"
}
],
"time": "2025-08-05T13:30:38+00:00"
},
{
"name": "doctrine/deprecations",
"version": "1.1.5",
@ -3179,16 +3037,16 @@
},
{
"name": "google/apiclient-services",
"version": "v0.409.0",
"version": "v0.410.0",
"source": {
"type": "git",
"url": "https://github.com/googleapis/google-api-php-client-services.git",
"reference": "8366037e450b62ffc1c5489459f207640acca2b4"
"reference": "ef335e912305403aef8a6552cc2101c537b9ebdd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/8366037e450b62ffc1c5489459f207640acca2b4",
"reference": "8366037e450b62ffc1c5489459f207640acca2b4",
"url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/ef335e912305403aef8a6552cc2101c537b9ebdd",
"reference": "ef335e912305403aef8a6552cc2101c537b9ebdd",
"shasum": ""
},
"require": {
@ -3217,9 +3075,9 @@
],
"support": {
"issues": "https://github.com/googleapis/google-api-php-client-services/issues",
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.409.0"
"source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.410.0"
},
"time": "2025-06-04T17:28:44+00:00"
"time": "2025-08-26T16:51:06+00:00"
},
{
"name": "google/auth",
@ -4047,91 +3905,52 @@
},
{
"name": "horstoeko/orderx",
"version": "dev-master",
"version": "v1.0.26",
"source": {
"type": "git",
"url": "https://github.com/turbo124/orderx.git",
"reference": "92b5f713ae3d07ba409f38218e1f0b131ad5fb05"
"url": "https://github.com/horstoeko/orderx.git",
"reference": "fa8b01a614a098e3478986426d5e1b98f3bc2747"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/turbo124/orderx/zipball/92b5f713ae3d07ba409f38218e1f0b131ad5fb05",
"reference": "92b5f713ae3d07ba409f38218e1f0b131ad5fb05",
"url": "https://api.github.com/repos/horstoeko/orderx/zipball/fa8b01a614a098e3478986426d5e1b98f3bc2747",
"reference": "fa8b01a614a098e3478986426d5e1b98f3bc2747",
"shasum": ""
},
"require": {
"adrienrn/php-mimetyper": "^0.2",
"ext-simplexml": "*",
"goetas-webservices/xsd2php-runtime": "^0.2.13",
"horstoeko/mimedb": "^1",
"horstoeko/stringmanagement": "^1",
"jms/serializer": "^3",
"php": "^7.3|^7.4|^8",
"php": ">=7.3",
"setasign/fpdf": "^1",
"setasign/fpdi": "^2",
"smalot/pdfparser": "^0",
"smalot/pdfparser": "^0|^2",
"symfony/process": "^5|^6|^7",
"symfony/validator": "^5|^6|^7",
"symfony/yaml": "^5|^6"
"symfony/yaml": "^5|^6|^7"
},
"require-dev": {
"goetas-webservices/xsd2php": "^0",
"nette/php-generator": "*",
"pdepend/pdepend": "^2",
"phpdocumentor/reflection-docblock": "^5",
"phploc/phploc": "^7",
"phpmd/phpmd": "^2",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan": "^1|^2",
"phpunit/phpunit": "^9",
"rector/rector": "*",
"sebastian/phpcpd": "^6",
"squizlabs/php_codesniffer": "^3"
},
"default-branch": true,
"type": "package",
"autoload": {
"psr-4": {
"horstoeko\\orderx\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"horstoeko\\orderx\\tests\\": "tests"
}
},
"scripts": {
"tests": [
"./vendor/bin/phpunit ./tests/"
],
"testsreal": [
"./vendor/bin/phpunit --configuration ./build/phpunit.xml"
],
"phpcs": [
"./vendor/bin/phpcs --standard=./build/phpcsrules.xml --extensions=php --ignore=autoload.php ./src ./tests"
],
"phpcs12": [
"./vendor/bin/phpcs --standard=./build/phpcsrules_psr12.xml --extensions=php --ignore=autoload.php ./src ./tests"
],
"phpcbf": [
"./vendor/bin/phpcbf -q ./src ./tests"
],
"phpcbf1": [
"./vendor/bin/phpcbf --standard=./build/phpcsrules_psr1.xml -q ./src ./tests"
],
"phpcbf2": [
"./vendor/bin/phpcbf --standard=./build/phpcsrules_psr2.xml -q ./src ./tests"
],
"phpcbf12": [
"./vendor/bin/phpcbf --standard=./build/phpcsrules_psr12.xml -q ./src ./tests"
],
"phpcbfsq": [
"./vendor/bin/phpcbf --standard=./build/phpcsrules_squiz.xml -q ./src ./tests"
],
"phpstan": [
"./vendor/bin/phpstan analyze -c ./build/phpstan.neon --autoload-file=vendor/autoload.php --no-interaction --no-progress --xdebug"
],
"phpstan_cs": [
"./vendor/bin/phpstan analyze -c ./build/phpstan.neon --autoload-file=vendor/autoload.php --no-interaction --no-progress --error-format=checkstyle --xdebug"
],
"makedoc": [
"phing -f ./build.xml projectdoc"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@ -4151,9 +3970,10 @@
"orderx"
],
"support": {
"source": "https://github.com/turbo124/orderx/tree/master"
"issues": "https://github.com/horstoeko/orderx/issues",
"source": "https://github.com/horstoeko/orderx/tree/v1.0.26"
},
"time": "2024-05-26T21:58:53+00:00"
"time": "2025-03-15T07:54:56+00:00"
},
{
"name": "horstoeko/stringmanagement",
@ -4717,89 +4537,35 @@
},
"time": "2025-02-27T23:34:14+00:00"
},
{
"name": "invoiceninja/inspector",
"version": "v3.0",
"source": {
"type": "git",
"url": "https://github.com/invoiceninja/inspector.git",
"reference": "29bc1ee7ae9d967287ecbd3485a2fee41a13e65f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/invoiceninja/inspector/zipball/29bc1ee7ae9d967287ecbd3485a2fee41a13e65f",
"reference": "29bc1ee7ae9d967287ecbd3485a2fee41a13e65f",
"shasum": ""
},
"require": {
"doctrine/dbal": "^4.0",
"illuminate/support": "^11.0",
"php": "^8.2"
},
"require-dev": {
"orchestra/testbench": "^9.1",
"phpunit/phpunit": "^11.1"
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"Inspector": "InvoiceNinja\\Inspector\\InspectorFacade"
},
"providers": [
"InvoiceNinja\\Inspector\\InspectorServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"InvoiceNinja\\Inspector\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Benjamin Beganović",
"email": "benjamin.beganovic4@outlook.com",
"role": "Developer"
}
],
"description": "Simplified database records management",
"homepage": "https://github.com/invoiceninja/inspector",
"keywords": [
"inspector",
"invoiceninja"
],
"support": {
"issues": "https://github.com/invoiceninja/inspector/issues",
"source": "https://github.com/invoiceninja/inspector/tree/v3.0"
},
"time": "2024-06-04T12:31:47+00:00"
},
{
"name": "invoiceninja/ubl_invoice",
"version": "v2.2.2",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/invoiceninja/UBL_invoice.git",
"reference": "7defd7e60363608df22bf3bc9caf749a29cde22c"
"reference": "5f346d927ec02ad8cf7e695a32e20bc6334141d5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/invoiceninja/UBL_invoice/zipball/7defd7e60363608df22bf3bc9caf749a29cde22c",
"reference": "7defd7e60363608df22bf3bc9caf749a29cde22c",
"url": "https://api.github.com/repos/invoiceninja/UBL_invoice/zipball/5f346d927ec02ad8cf7e695a32e20bc6334141d5",
"reference": "5f346d927ec02ad8cf7e695a32e20bc6334141d5",
"shasum": ""
},
"require": {
"dallgoot/yaml": "0.9.1.2",
"goetas-webservices/xsd2php-runtime": "^0.2.17",
"illuminate/collections": "^10|^11|^12",
"sabre/xml": "^4.0"
},
"require-dev": {
"genkgo/xsl": "^1.3",
"goetas-webservices/xsd-reader": "^0.4.4",
"goetas-webservices/xsd2php": "^0.4.13",
"greenter/ubl-validator": "^2",
"milo/schematron": "^1.0",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^10"
"phpunit/phpunit": "^10",
"symfony/console": "^6.4"
},
"type": "library",
"autoload": {
@ -4838,9 +4604,9 @@
"xml invoice"
],
"support": {
"source": "https://github.com/invoiceninja/UBL_invoice/tree/v2.2.2"
"source": "https://github.com/invoiceninja/UBL_invoice/tree/v3.0.1"
},
"time": "2024-04-10T11:53:16+00:00"
"time": "2025-08-30T23:39:57+00:00"
},
{
"name": "jean85/pretty-package-versions",
@ -7507,16 +7273,16 @@
},
{
"name": "mindee/mindee",
"version": "v1.12.0",
"version": "v1.22.0",
"source": {
"type": "git",
"url": "https://github.com/mindee/mindee-api-php.git",
"reference": "4088e5d7e5aef72162dbea10386c64c3f071aa23"
"reference": "e3e202309d4b23bb3ecd468450ee5ec567bf0dba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mindee/mindee-api-php/zipball/4088e5d7e5aef72162dbea10386c64c3f071aa23",
"reference": "4088e5d7e5aef72162dbea10386c64c3f071aa23",
"url": "https://api.github.com/repos/mindee/mindee-api-php/zipball/e3e202309d4b23bb3ecd468450ee5ec567bf0dba",
"reference": "e3e202309d4b23bb3ecd468450ee5ec567bf0dba",
"shasum": ""
},
"require": {
@ -7526,6 +7292,7 @@
"php": ">=7.4",
"setasign/fpdf": "^1.8",
"setasign/fpdi": "^2.6",
"smalot/pdfparser": "^2.11",
"symfony/console": ">=5.4"
},
"require-dev": {
@ -7555,9 +7322,9 @@
"description": "Mindee Client Library for PHP",
"support": {
"issues": "https://github.com/mindee/mindee-api-php/issues",
"source": "https://github.com/mindee/mindee-api-php/tree/v1.12.0"
"source": "https://github.com/mindee/mindee-api-php/tree/v1.22.0"
},
"time": "2024-10-11T15:47:16+00:00"
"time": "2025-06-04T07:47:11+00:00"
},
{
"name": "mollie/mollie-api-php",
@ -10044,16 +9811,16 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "2.2.0",
"version": "2.3.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8"
"reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b9e61a61e39e02dd90944e9115241c7f7e76bfd8",
"reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495",
"reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495",
"shasum": ""
},
"require": {
@ -10085,9 +9852,9 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/2.2.0"
"source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0"
},
"time": "2025-07-13T07:04:09+00:00"
"time": "2025-08-30T15:50:23+00:00"
},
{
"name": "pragmarx/google2fa",
@ -11706,27 +11473,24 @@
},
{
"name": "smalot/pdfparser",
"version": "v0.19.0",
"version": "v2.12.1",
"source": {
"type": "git",
"url": "https://github.com/smalot/pdfparser.git",
"reference": "1895c17417aefa4508e35836c46da61988d61f26"
"reference": "98d31ba34ef5b5a98897ef4b6c3925d502ea53b1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/smalot/pdfparser/zipball/1895c17417aefa4508e35836c46da61988d61f26",
"reference": "1895c17417aefa4508e35836c46da61988d61f26",
"url": "https://api.github.com/repos/smalot/pdfparser/zipball/98d31ba34ef5b5a98897ef4b6c3925d502ea53b1",
"reference": "98d31ba34ef5b5a98897ef4b6c3925d502ea53b1",
"shasum": ""
},
"require": {
"ext-iconv": "*",
"ext-zlib": "*",
"php": ">=5.6",
"php": ">=7.1",
"symfony/polyfill-mbstring": "^1.18"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.16",
"symfony/phpunit-bridge": "^5.2"
},
"type": "library",
"autoload": {
"psr-0": {
@ -11754,9 +11518,9 @@
],
"support": {
"issues": "https://github.com/smalot/pdfparser/issues",
"source": "https://github.com/smalot/pdfparser/tree/v0.19.0"
"source": "https://github.com/smalot/pdfparser/tree/v2.12.1"
},
"time": "2021-04-13T08:27:56+00:00"
"time": "2025-07-31T06:19:56+00:00"
},
{
"name": "socialiteproviders/apple",
@ -13572,16 +13336,16 @@
},
{
"name": "symfony/http-foundation",
"version": "v7.3.2",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6"
"reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/6877c122b3a6cc3695849622720054f6e6fa5fa6",
"reference": "6877c122b3a6cc3695849622720054f6e6fa5fa6",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/7475561ec27020196c49bb7c4f178d33d7d3dc00",
"reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00",
"shasum": ""
},
"require": {
@ -13631,7 +13395,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v7.3.2"
"source": "https://github.com/symfony/http-foundation/tree/v7.3.3"
},
"funding": [
{
@ -13651,7 +13415,7 @@
"type": "tidelift"
}
],
"time": "2025-07-10T08:47:49+00:00"
"time": "2025-08-20T08:04:18+00:00"
},
{
"name": "symfony/http-kernel",
@ -16642,28 +16406,28 @@
},
{
"name": "symfony/yaml",
"version": "v6.4.25",
"version": "v7.3.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "e54b060bc9c3dc3d4258bf0d165d0064e755f565"
"reference": "d4f4a66866fe2451f61296924767280ab5732d9d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/e54b060bc9c3dc3d4258bf0d165d0064e755f565",
"reference": "e54b060bc9c3dc3d4258bf0d165d0064e755f565",
"url": "https://api.github.com/repos/symfony/yaml/zipball/d4f4a66866fe2451f61296924767280ab5732d9d",
"reference": "d4f4a66866fe2451f61296924767280ab5732d9d",
"shasum": ""
},
"require": {
"php": ">=8.1",
"symfony/deprecation-contracts": "^2.5|^3",
"php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3.0",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
"symfony/console": "<5.4"
"symfony/console": "<6.4"
},
"require-dev": {
"symfony/console": "^5.4|^6.0|^7.0"
"symfony/console": "^6.4|^7.0"
},
"bin": [
"Resources/bin/yaml-lint"
@ -16694,7 +16458,7 @@
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/yaml/tree/v6.4.25"
"source": "https://github.com/symfony/yaml/tree/v7.3.3"
},
"funding": [
{
@ -16714,7 +16478,7 @@
"type": "tidelift"
}
],
"time": "2025-08-26T16:59:00+00:00"
"time": "2025-08-27T11:34:33+00:00"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
@ -16773,26 +16537,29 @@
},
{
"name": "turbo124/beacon",
"version": "v2.0.4",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/turbo124/beacon.git",
"reference": "17769e5b29b9a5e9db5fabd896e0af9e64738dbb"
"reference": "3a2d4edc82a190230ccabce25a586b01120b5b66"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/turbo124/beacon/zipball/17769e5b29b9a5e9db5fabd896e0af9e64738dbb",
"reference": "17769e5b29b9a5e9db5fabd896e0af9e64738dbb",
"url": "https://api.github.com/repos/turbo124/beacon/zipball/3a2d4edc82a190230ccabce25a586b01120b5b66",
"reference": "3a2d4edc82a190230ccabce25a586b01120b5b66",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^7",
"illuminate/support": "^9.0|^10.0|^11|^12",
"php": "^8",
"illuminate/support": "^11|^12",
"php": "^8.2|^8.3",
"turbo124/waffy": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^10.0"
"larastan/larastan": "^3.6",
"orchestra/testbench": "^10.6",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11"
},
"type": "library",
"extra": {
@ -16830,9 +16597,9 @@
"turbo124"
],
"support": {
"source": "https://github.com/turbo124/beacon/tree/v2.0.4"
"source": "https://github.com/turbo124/beacon/tree/v3.0.1"
},
"time": "2025-04-16T10:54:18+00:00"
"time": "2025-09-01T02:13:38+00:00"
},
{
"name": "turbo124/waffy",
@ -21037,16 +20804,16 @@
},
{
"name": "spatie/backtrace",
"version": "1.8.0",
"version": "1.8.1",
"source": {
"type": "git",
"url": "https://github.com/spatie/backtrace.git",
"reference": "1607d8870bf597fc4ad79a6945cf0b2e584c2669"
"reference": "8c0f16a59ae35ec8c62d85c3c17585158f430110"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/backtrace/zipball/1607d8870bf597fc4ad79a6945cf0b2e584c2669",
"reference": "1607d8870bf597fc4ad79a6945cf0b2e584c2669",
"url": "https://api.github.com/repos/spatie/backtrace/zipball/8c0f16a59ae35ec8c62d85c3c17585158f430110",
"reference": "8c0f16a59ae35ec8c62d85c3c17585158f430110",
"shasum": ""
},
"require": {
@ -21085,7 +20852,7 @@
],
"support": {
"issues": "https://github.com/spatie/backtrace/issues",
"source": "https://github.com/spatie/backtrace/tree/1.8.0"
"source": "https://github.com/spatie/backtrace/tree/1.8.1"
},
"funding": [
{
@ -21097,7 +20864,7 @@
"type": "other"
}
],
"time": "2025-08-25T16:16:45+00:00"
"time": "2025-08-26T08:22:30+00:00"
},
{
"name": "spatie/error-solutions",
@ -21728,7 +21495,6 @@
"stability-flags": {
"asm/php-ansible": 20,
"beganovich/snappdf": 20,
"horstoeko/orderx": 20,
"invoiceninja/einvoice": 20,
"socialiteproviders/apple": 20
},

View File

@ -200,6 +200,7 @@ return [
App\Providers\ClientPortalServiceProvider::class,
App\Providers\NinjaTranslationServiceProvider::class,
App\Providers\StaticServiceProvider::class,
App\Providers\ScoutServiceProvider::class,
],
/*

View File

@ -10,8 +10,7 @@ return [
/*
* The API endpoint for logs
*/
'endpoint' => 'https://app.lightlogs.com/api',
'endpoint' => env('BEACON_API_URL', 'https://app.lightlogs.com/api'), // ,
/*
* Your API key
*/

View File

@ -51,6 +51,7 @@ return [
'transport' => 'ses',
],
'mailgun' => [
'transport' => 'mailgun',
],

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.12.22'),
'app_tag' => env('APP_TAG', '5.12.22'),
'app_version' => env('APP_VERSION', '5.12.23'),
'app_tag' => env('APP_TAG', '5.12.23'),
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', false),
@ -257,7 +257,7 @@ return [
'storecove_email_catchall' => env('STORECOVE_CATCHALL_EMAIL',false),
'qvalia_api_key' => env('QVALIA_API_KEY', false),
'qvalia_partner_number' => env('QVALIA_PARTNER_NUMBER', false),
'pdf_page_numbering_x_alignment' => env('PDF_PAGE_NUMBER_X', -10),
'pdf_page_numbering_x_alignment' => env('PDF_PAGE_NUMBER_X', -5),
'pdf_page_numbering_y_alignment' => env('PDF_PAGE_NUMBER_Y', -6),
'hosted_einvoice_secret' => env('HOSTED_EINVOICE_SECRET', null),
'e_invoice_quota_warning' => env('E_INVOICE_QUOTA_WARNING', 15),

View File

@ -28,6 +28,7 @@ class CreditFactory extends Factory
'status_id' => Credit::STATUS_DRAFT,
'discount' => $this->faker->numberBetween(1, 10),
'is_amount_discount' => (bool) random_int(0, 1),
'number' => \Illuminate\Support\Str::random(54),
'tax_name1' => 'GST',
'tax_rate1' => 10,
'tax_name2' => 'VAT',

View File

@ -0,0 +1,45 @@
<?php
use App\Models\Language;
use Illuminate\Support\Facades\Cache;
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
{
Language::unguard();
if (!Language::find(43)) {
Language::create(['id' => 43, 'name' => 'Catalan', 'locale' => 'ca']);
}
if (!Language::find(44)) {
Language::create(['id' => 44, 'name' => 'Afrikaans', 'locale' => 'af_ZA']);
}
if(!Language::find(45)) {
Language::create(['id' => 45, 'name' => 'Indonesian', 'locale' => 'id_ID']);
}
$resource = Language::query()->orderBy('name')->get();
Cache::forever('languages', $resource);
}
/**
* Reverse the migrations.
*/
public function down(): void
{
//
}
};

View File

@ -67,6 +67,10 @@ class LanguageSeeder extends Seeder
['id' => 40, 'name' => 'French - Swiss', 'locale' => 'fr_CH'],
['id' => 41, 'name' => 'Lao', 'locale' => 'lo_LA'],
['id' => 42, 'name' => 'Vietnamese', 'locale' => 'vi'],
['id' => 43, 'name' => 'Catalan', 'locale' => 'ca'],
['id' => 44, 'name' => 'Afrikaans', 'locale' => 'af_ZA'],
['id' => 45, 'name' => 'Indonesian', 'locale' => 'id_ID'],
];
foreach ($languages as $language) {

View File

@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
use Elastic\Adapter\Indices\Mapping;
use Elastic\Adapter\Indices\Settings;
use Elastic\Migrations\Facades\Index;
use Elastic\Migrations\MigrationInterface;
final class CreateInvoicesIndex implements MigrationInterface
{
/**
* Run the migration.
*/
public function up(): void
{
$mapping = [
'properties' => [
// Core invoice fields
'id' => ['type' => 'keyword'],
'name' => [
'type' => 'text',
'analyzer' => 'standard'
],
'hashed_id' => ['type' => 'keyword'],
'number' => ['type' => 'keyword'],
'is_deleted' => ['type' => 'boolean'],
'amount' => ['type' => 'float'],
'balance' => ['type' => 'float'],
'due_date' => ['type' => 'date'],
'date' => ['type' => 'date'],
// Custom fields
'custom_value1' => ['type' => 'keyword'],
'custom_value2' => ['type' => 'keyword'],
'custom_value3' => ['type' => 'keyword'],
'custom_value4' => ['type' => 'keyword'],
// Additional fields
'company_key' => ['type' => 'keyword'],
'po_number' => ['type' => 'keyword'],
// Line items
'line_items' => [
'type' => 'nested',
'properties' => [
'quantity' => ['type' => 'float'],
'net_cost' => ['type' => 'float'],
'cost' => ['type' => 'float'],
'product_key' => ['type' => 'text', 'analyzer' => 'standard'],
'product_cost' => ['type' => 'float'],
'notes' => ['type' => 'text', 'analyzer' => 'standard'],
'discount' => ['type' => 'float'],
'is_amount_discount' => ['type' => 'boolean'],
'tax_name1' => ['type' => 'keyword'],
'tax_rate1' => ['type' => 'float'],
'tax_name2' => ['type' => 'keyword'],
'tax_rate2' => ['type' => 'float'],
'tax_name3' => ['type' => 'keyword'],
'tax_rate3' => ['type' => 'float'],
'sort_id' => ['type' => 'keyword'],
'line_total' => ['type' => 'float'],
'gross_line_total' => ['type' => 'float'],
'tax_amount' => ['type' => 'float'],
'date' => ['type' => 'keyword'],
'custom_value1' => ['type' => 'keyword'],
'custom_value2' => ['type' => 'keyword'],
'custom_value3' => ['type' => 'keyword'],
'custom_value4' => ['type' => 'keyword'],
'type_id' => ['type' => 'keyword'],
'tax_id' => ['type' => 'keyword'],
'task_id' => ['type' => 'keyword'],
'expense_id' => ['type' => 'keyword'],
'unit_code' => ['type' => 'keyword'],
]
],
]
];
Index::createRaw('invoices_v2', $mapping);
}
/**
* Reverse the migration.
*/
public function down(): void
{
Index::dropIfExists('invoices_v2');
}
}

View File

@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
use Elastic\Adapter\Indices\Mapping;
use Elastic\Adapter\Indices\Settings;
use Elastic\Migrations\Facades\Index;
use Elastic\Migrations\MigrationInterface;
final class CreateQuotesIndex implements MigrationInterface
{
/**
* Run the migration.
*/
public function up(): void
{
$mapping = [
'properties' => [
// Core quote fields
'id' => ['type' => 'keyword'],
'name' => [
'type' => 'text',
'analyzer' => 'standard'
],
'hashed_id' => ['type' => 'keyword'],
'number' => ['type' => 'keyword'],
'is_deleted' => ['type' => 'boolean'],
'amount' => ['type' => 'float'],
'balance' => ['type' => 'float'],
'due_date' => ['type' => 'date'],
'date' => ['type' => 'date'],
// Custom fields
'custom_value1' => ['type' => 'keyword'],
'custom_value2' => ['type' => 'keyword'],
'custom_value3' => ['type' => 'keyword'],
'custom_value4' => ['type' => 'keyword'],
// Additional fields
'company_key' => ['type' => 'keyword'],
'po_number' => ['type' => 'keyword'],
// Line items
'line_items' => [
'type' => 'nested',
'properties' => [
'quantity' => ['type' => 'float'],
'net_cost' => ['type' => 'float'],
'cost' => ['type' => 'float'],
'product_key' => ['type' => 'text', 'analyzer' => 'standard'],
'product_cost' => ['type' => 'float'],
'notes' => ['type' => 'text', 'analyzer' => 'standard'],
'discount' => ['type' => 'float'],
'is_amount_discount' => ['type' => 'boolean'],
'tax_name1' => ['type' => 'keyword'],
'tax_rate1' => ['type' => 'float'],
'tax_name2' => ['type' => 'keyword'],
'tax_rate2' => ['type' => 'float'],
'tax_name3' => ['type' => 'keyword'],
'tax_rate3' => ['type' => 'float'],
'sort_id' => ['type' => 'keyword'],
'line_total' => ['type' => 'float'],
'gross_line_total' => ['type' => 'float'],
'tax_amount' => ['type' => 'float'],
'date' => ['type' => 'keyword'],
'custom_value1' => ['type' => 'keyword'],
'custom_value2' => ['type' => 'keyword'],
'custom_value3' => ['type' => 'keyword'],
'custom_value4' => ['type' => 'keyword'],
'type_id' => ['type' => 'keyword'],
'tax_id' => ['type' => 'keyword'],
'task_id' => ['type' => 'keyword'],
'expense_id' => ['type' => 'keyword'],
'unit_code' => ['type' => 'keyword'],
]
],
]
];
Index::createRaw('quotes_v2', $mapping);
}
/**
* Reverse the migration.
*/
public function down(): void
{
Index::dropIfExists('quotes_v2');
}
}

View File

@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
use Elastic\Adapter\Indices\Mapping;
use Elastic\Adapter\Indices\Settings;
use Elastic\Migrations\Facades\Index;
use Elastic\Migrations\MigrationInterface;
final class CreateCreditsIndex implements MigrationInterface
{
/**
* Run the migration.
*/
public function up(): void
{
$mapping = [
'properties' => [
// Core credit fields
'id' => ['type' => 'keyword'],
'name' => [
'type' => 'text',
'analyzer' => 'standard'
],
'hashed_id' => ['type' => 'keyword'],
'number' => ['type' => 'keyword'],
'is_deleted' => ['type' => 'boolean'],
'amount' => ['type' => 'float'],
'balance' => ['type' => 'float'],
'due_date' => ['type' => 'date'],
'date' => ['type' => 'date'],
// Custom fields
'custom_value1' => ['type' => 'keyword'],
'custom_value2' => ['type' => 'keyword'],
'custom_value3' => ['type' => 'keyword'],
'custom_value4' => ['type' => 'keyword'],
// Additional fields
'company_key' => ['type' => 'keyword'],
'po_number' => ['type' => 'keyword'],
// Line items
'line_items' => [
'type' => 'nested',
'properties' => [
'quantity' => ['type' => 'float'],
'net_cost' => ['type' => 'float'],
'cost' => ['type' => 'float'],
'product_key' => ['type' => 'text', 'analyzer' => 'standard'],
'product_cost' => ['type' => 'float'],
'notes' => ['type' => 'text', 'analyzer' => 'standard'],
'discount' => ['type' => 'float'],
'is_amount_discount' => ['type' => 'boolean'],
'tax_name1' => ['type' => 'keyword'],
'tax_rate1' => ['type' => 'float'],
'tax_name2' => ['type' => 'keyword'],
'tax_rate2' => ['type' => 'float'],
'tax_name3' => ['type' => 'keyword'],
'tax_rate3' => ['type' => 'float'],
'sort_id' => ['type' => 'keyword'],
'line_total' => ['type' => 'float'],
'gross_line_total' => ['type' => 'float'],
'tax_amount' => ['type' => 'float'],
'date' => ['type' => 'keyword'],
'custom_value1' => ['type' => 'keyword'],
'custom_value2' => ['type' => 'keyword'],
'custom_value3' => ['type' => 'keyword'],
'custom_value4' => ['type' => 'keyword'],
'type_id' => ['type' => 'keyword'],
'tax_id' => ['type' => 'keyword'],
'task_id' => ['type' => 'keyword'],
'expense_id' => ['type' => 'keyword'],
'unit_code' => ['type' => 'keyword'],
]
],
]
];
Index::createRaw('credits_v2', $mapping);
}
/**
* Reverse the migration.
*/
public function down(): void
{
Index::dropIfExists('credits_v2');
}
}

View File

@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
use Elastic\Adapter\Indices\Mapping;
use Elastic\Adapter\Indices\Settings;
use Elastic\Migrations\Facades\Index;
use Elastic\Migrations\MigrationInterface;
final class CreateRecurringInvoicesIndex implements MigrationInterface
{
/**
* Run the migration.
*/
public function up(): void
{
// Force drop any existing indices to avoid mapping conflicts
Index::dropIfExists('recurring_invoices_v2');
Index::dropIfExists('recurring_invoices');
$mapping = [
'properties' => [
// Core recurring invoice fields
'id' => ['type' => 'keyword'],
'name' => [
'type' => 'text',
'analyzer' => 'standard'
],
'hashed_id' => ['type' => 'keyword'],
'number' => ['type' => 'keyword'],
'is_deleted' => ['type' => 'boolean'],
'amount' => ['type' => 'float'],
'balance' => ['type' => 'float'],
'due_date' => ['type' => 'date'],
'date' => ['type' => 'date'],
// Custom fields
'custom_value1' => ['type' => 'keyword'],
'custom_value2' => ['type' => 'keyword'],
'custom_value3' => ['type' => 'keyword'],
'custom_value4' => ['type' => 'keyword'],
// Additional fields
'company_key' => ['type' => 'keyword'],
'po_number' => ['type' => 'keyword'],
// Line items
'line_items' => [
'type' => 'nested',
'properties' => [
'quantity' => ['type' => 'float'],
'net_cost' => ['type' => 'float'],
'cost' => ['type' => 'float'],
'product_key' => ['type' => 'text', 'analyzer' => 'standard'],
'product_cost' => ['type' => 'float'],
'notes' => ['type' => 'text', 'analyzer' => 'standard'],
'discount' => ['type' => 'float'],
'is_amount_discount' => ['type' => 'boolean'],
'tax_name1' => ['type' => 'keyword'],
'tax_rate1' => ['type' => 'float'],
'tax_name2' => ['type' => 'keyword'],
'tax_rate2' => ['type' => 'float'],
'tax_name3' => ['type' => 'keyword'],
'tax_rate3' => ['type' => 'float'],
'sort_id' => ['type' => 'keyword'],
'line_total' => ['type' => 'float'],
'gross_line_total' => ['type' => 'float'],
'tax_amount' => ['type' => 'float'],
'date' => ['type' => 'keyword'],
'custom_value1' => ['type' => 'keyword'],
'custom_value2' => ['type' => 'keyword'],
'custom_value3' => ['type' => 'keyword'],
'custom_value4' => ['type' => 'keyword'],
'type_id' => ['type' => 'keyword'],
'tax_id' => ['type' => 'keyword'],
'task_id' => ['type' => 'keyword'],
'expense_id' => ['type' => 'keyword'],
'unit_code' => ['type' => 'keyword'],
]
],
]
];
Index::createRaw('recurring_invoices_v2', $mapping);
}
/**
* Reverse the migration.
*/
public function down(): void
{
Index::dropIfExists('recurring_invoices_v2');
}
}

View File

@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
use Elastic\Adapter\Indices\Mapping;
use Elastic\Adapter\Indices\Settings;
use Elastic\Migrations\Facades\Index;
use Elastic\Migrations\MigrationInterface;
final class CreatePurchaseOrdersIndex implements MigrationInterface
{
/**
* Run the migration.
*/
public function up(): void
{
$mapping = [
'properties' => [
// Core purchase order fields
'id' => ['type' => 'keyword'],
'name' => [
'type' => 'text',
'analyzer' => 'standard'
],
'hashed_id' => ['type' => 'keyword'],
'number' => ['type' => 'keyword'],
'is_deleted' => ['type' => 'boolean'],
'amount' => ['type' => 'float'],
'balance' => ['type' => 'float'],
'due_date' => ['type' => 'date'],
'date' => ['type' => 'date'],
// Custom fields
'custom_value1' => ['type' => 'keyword'],
'custom_value2' => ['type' => 'keyword'],
'custom_value3' => ['type' => 'keyword'],
'custom_value4' => ['type' => 'keyword'],
// Additional fields
'company_key' => ['type' => 'keyword'],
'po_number' => ['type' => 'keyword'],
// Line items
'line_items' => [
'type' => 'nested',
'properties' => [
'quantity' => ['type' => 'float'],
'net_cost' => ['type' => 'float'],
'cost' => ['type' => 'float'],
'product_key' => ['type' => 'text', 'analyzer' => 'standard'],
'product_cost' => ['type' => 'float'],
'notes' => ['type' => 'text', 'analyzer' => 'standard'],
'discount' => ['type' => 'float'],
'is_amount_discount' => ['type' => 'boolean'],
'tax_name1' => ['type' => 'keyword'],
'tax_rate1' => ['type' => 'float'],
'tax_name2' => ['type' => 'keyword'],
'tax_rate2' => ['type' => 'float'],
'tax_name3' => ['type' => 'keyword'],
'tax_rate3' => ['type' => 'float'],
'sort_id' => ['type' => 'keyword'],
'line_total' => ['type' => 'float'],
'gross_line_total' => ['type' => 'float'],
'tax_amount' => ['type' => 'float'],
'date' => ['type' => 'keyword'],
'custom_value1' => ['type' => 'keyword'],
'custom_value2' => ['type' => 'keyword'],
'custom_value3' => ['type' => 'keyword'],
'custom_value4' => ['type' => 'keyword'],
'type_id' => ['type' => 'keyword'],
'tax_id' => ['type' => 'keyword'],
'task_id' => ['type' => 'keyword'],
'expense_id' => ['type' => 'keyword'],
'unit_code' => ['type' => 'keyword'],
]
],
]
];
Index::createRaw('purchase_orders_v2', $mapping);
}
/**
* Reverse the migration.
*/
public function down(): void
{
Index::dropIfExists('purchase_orders_v2');
}
}

View File

@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
use Elastic\Adapter\Indices\Mapping;
use Elastic\Adapter\Indices\Settings;
use Elastic\Migrations\Facades\Index;
use Elastic\Migrations\MigrationInterface;
final class CreateVendorsIndex implements MigrationInterface
{
/**
* Run the migration.
*/
public function up(): void
{
$mapping = [
'properties' => [
// Core vendor fields
'id' => ['type' => 'keyword'],
'name' => [
'type' => 'text',
'analyzer' => 'standard'
],
'is_deleted' => ['type' => 'boolean'],
'hashed_id' => ['type' => 'keyword'],
'number' => ['type' => 'keyword'],
'id_number' => ['type' => 'keyword'],
'vat_number' => ['type' => 'keyword'],
// Contact information
'phone' => ['type' => 'keyword'],
// Address fields
'address1' => ['type' => 'keyword'],
'address2' => ['type' => 'keyword'],
'city' => ['type' => 'keyword'],
'state' => ['type' => 'keyword'],
'postal_code' => ['type' => 'keyword'],
// Additional fields
'website' => ['type' => 'keyword'],
'private_notes' => [
'type' => 'text',
'analyzer' => 'standard'
],
'public_notes' => [
'type' => 'text',
'analyzer' => 'standard'
],
// Custom fields
'custom_value1' => ['type' => 'keyword'],
'custom_value2' => ['type' => 'keyword'],
'custom_value3' => ['type' => 'keyword'],
'custom_value4' => ['type' => 'keyword'],
// Company
'company_key' => ['type' => 'keyword'],
]
];
Index::createRaw('vendors_v2', $mapping);
}
/**
* Reverse the migration.
*/
public function down(): void
{
Index::dropIfExists('vendors_v2');
}
}

View File

@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
use Elastic\Adapter\Indices\Mapping;
use Elastic\Adapter\Indices\Settings;
use Elastic\Migrations\Facades\Index;
use Elastic\Migrations\MigrationInterface;
final class CreateExpensesIndex implements MigrationInterface
{
/**
* Run the migration.
*/
public function up(): void
{
$mapping = [
'properties' => [
// Core expense fields
'id' => ['type' => 'keyword'],
'name' => [
'type' => 'text',
'analyzer' => 'standard'
],
'is_deleted' => ['type' => 'boolean'],
'hashed_id' => ['type' => 'keyword'],
'number' => ['type' => 'keyword'],
'amount' => ['type' => 'float'],
'tax_amount' => ['type' => 'float'],
'tax_name1' => ['type' => 'keyword'],
'tax_rate1' => ['type' => 'float'],
'tax_name2' => ['type' => 'keyword'],
'tax_rate2' => ['type' => 'float'],
'tax_name3' => ['type' => 'keyword'],
'tax_rate3' => ['type' => 'float'],
'date' => ['type' => 'date'],
'payment_date' => ['type' => 'date'],
// Custom fields
'custom_value1' => ['type' => 'keyword'],
'custom_value2' => ['type' => 'keyword'],
'custom_value3' => ['type' => 'keyword'],
'custom_value4' => ['type' => 'keyword'],
// Additional fields
'company_key' => ['type' => 'keyword'],
'category_id' => ['type' => 'keyword'],
'vendor_id' => ['type' => 'keyword'],
'client_id' => ['type' => 'keyword'],
'project_id' => ['type' => 'keyword'],
'private_notes' => [
'type' => 'text',
'analyzer' => 'standard'
],
'public_notes' => [
'type' => 'text',
'analyzer' => 'standard'
],
]
];
Index::createRaw('expenses_v2', $mapping);
}
/**
* Reverse the migration.
*/
public function down(): void
{
Index::dropIfExists('expenses_v2');
}
}

View File

@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
use Elastic\Adapter\Indices\Mapping;
use Elastic\Adapter\Indices\Settings;
use Elastic\Migrations\Facades\Index;
use Elastic\Migrations\MigrationInterface;
final class CreateProjectsIndex implements MigrationInterface
{
/**
* Run the migration.
*/
public function up(): void
{
$mapping = [
'properties' => [
// Core project fields
'id' => ['type' => 'keyword'],
'name' => [
'type' => 'text',
'analyzer' => 'standard'
],
'is_deleted' => ['type' => 'boolean'],
'hashed_id' => ['type' => 'keyword'],
'number' => ['type' => 'keyword'],
'description' => [
'type' => 'text',
'analyzer' => 'standard'
],
'budgeted_hours' => ['type' => 'float'],
'task_rate' => ['type' => 'float'],
'due_date' => ['type' => 'date'],
'start_date' => ['type' => 'date'],
// Custom fields
'custom_value1' => ['type' => 'keyword'],
'custom_value2' => ['type' => 'keyword'],
'custom_value3' => ['type' => 'keyword'],
'custom_value4' => ['type' => 'keyword'],
// Additional fields
'company_key' => ['type' => 'keyword'],
'client_id' => ['type' => 'keyword'],
'assigned_user_id' => ['type' => 'keyword'],
'private_notes' => [
'type' => 'text',
'analyzer' => 'standard'
],
'public_notes' => [
'type' => 'text',
'analyzer' => 'standard'
],
]
];
Index::createRaw('projects_v2', $mapping);
}
/**
* Reverse the migration.
*/
public function down(): void
{
Index::dropIfExists('projects_v2');
}
}

View File

@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
use Elastic\Adapter\Indices\Mapping;
use Elastic\Adapter\Indices\Settings;
use Elastic\Migrations\Facades\Index;
use Elastic\Migrations\MigrationInterface;
final class CreateTasksIndex implements MigrationInterface
{
/**
* Run the migration.
*/
public function up(): void
{
$mapping = [
'properties' => [
// Core task fields
'id' => ['type' => 'keyword'],
'name' => [
'type' => 'text',
'analyzer' => 'standard'
],
'is_deleted' => ['type' => 'boolean'],
'hashed_id' => ['type' => 'keyword'],
'number' => ['type' => 'keyword'],
'description' => [
'type' => 'text',
'analyzer' => 'standard'
],
'rate' => ['type' => 'float'],
'hours' => ['type' => 'float'],
'due_date' => ['type' => 'date'],
'start_date' => ['type' => 'date'],
// Custom fields
'custom_value1' => ['type' => 'keyword'],
'custom_value2' => ['type' => 'keyword'],
'custom_value3' => ['type' => 'keyword'],
'custom_value4' => ['type' => 'keyword'],
// Additional fields
'company_key' => ['type' => 'keyword'],
'client_id' => ['type' => 'keyword'],
'project_id' => ['type' => 'keyword'],
'assigned_user_id' => ['type' => 'keyword'],
'status_id' => ['type' => 'keyword'],
'private_notes' => [
'type' => 'text',
'analyzer' => 'standard'
],
'public_notes' => [
'type' => 'text',
'analyzer' => 'standard'
],
]
];
Index::createRaw('tasks_v2', $mapping);
}
/**
* Reverse the migration.
*/
public function down(): void
{
Index::dropIfExists('tasks_v2');
}
}

View File

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
use Elastic\Adapter\Indices\Mapping;
use Elastic\Adapter\Indices\Settings;
use Elastic\Migrations\Facades\Index;
use Elastic\Migrations\MigrationInterface;
final class CreateClientContactsIndex implements MigrationInterface
{
/**
* Run the migration.
*/
public function up(): void
{
$mapping = [
'properties' => [
// Core client contact fields
'id' => ['type' => 'keyword'],
'name' => [
'type' => 'text',
'analyzer' => 'standard'
],
'is_deleted' => ['type' => 'boolean'],
'hashed_id' => ['type' => 'keyword'],
'first_name' => ['type' => 'keyword'],
'last_name' => ['type' => 'keyword'],
'email' => ['type' => 'keyword'],
'phone' => ['type' => 'keyword'],
'is_primary' => ['type' => 'boolean'],
// Custom fields
'custom_value1' => ['type' => 'keyword'],
'custom_value2' => ['type' => 'keyword'],
'custom_value3' => ['type' => 'keyword'],
'custom_value4' => ['type' => 'keyword'],
// Additional fields
'company_key' => ['type' => 'keyword'],
'client_id' => ['type' => 'keyword'],
'send_email' => ['type' => 'boolean'],
'last_login' => ['type' => 'date'],
]
];
Index::createRaw('client_contacts_v2', $mapping);
}
/**
* Reverse the migration.
*/
public function down(): void
{
Index::dropIfExists('client_contacts_v2');
}
}

View File

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
use Elastic\Adapter\Indices\Mapping;
use Elastic\Adapter\Indices\Settings;
use Elastic\Migrations\Facades\Index;
use Elastic\Migrations\MigrationInterface;
final class CreateVendorContactsIndex implements MigrationInterface
{
/**
* Run the migration.
*/
public function up(): void
{
$mapping = [
'properties' => [
// Core vendor contact fields
'id' => ['type' => 'keyword'],
'name' => [
'type' => 'text',
'analyzer' => 'standard'
],
'is_deleted' => ['type' => 'boolean'],
'hashed_id' => ['type' => 'keyword'],
'first_name' => ['type' => 'keyword'],
'last_name' => ['type' => 'keyword'],
'email' => ['type' => 'keyword'],
'phone' => ['type' => 'keyword'],
'is_primary' => ['type' => 'boolean'],
// Custom fields
'custom_value1' => ['type' => 'keyword'],
'custom_value2' => ['type' => 'keyword'],
'custom_value3' => ['type' => 'keyword'],
'custom_value4' => ['type' => 'keyword'],
// Additional fields
'company_key' => ['type' => 'keyword'],
'vendor_id' => ['type' => 'keyword'],
'send_email' => ['type' => 'boolean'],
]
];
Index::createRaw('vendor_contacts_v2', $mapping);
}
/**
* Reverse the migration.
*/
public function down(): void
{
Index::dropIfExists('vendor_contacts_v2');
}
}

View File

@ -0,0 +1,146 @@
<?php
declare(strict_types=1);
use Elastic\Adapter\Indices\Mapping;
use Elastic\Adapter\Indices\Settings;
use Elastic\Migrations\Facades\Index;
use Elastic\Migrations\MigrationInterface;
final class CreateClientsIndex implements MigrationInterface
{
/**
* Run the migration.
*/
public function up(): void
{
$mapping = [
'properties' => [
// Core client fields
'id' => ['type' => 'keyword'],
'name' => [
'type' => 'text',
'analyzer' => 'standard'
],
'hashed_id' => ['type' => 'keyword'],
'number' => ['type' => 'keyword'],
'is_deleted' => ['type' => 'boolean'],
'user_id' => ['type' => 'keyword'],
'assigned_user_id' => ['type' => 'keyword'],
'company_id' => ['type' => 'keyword'],
// Contact and business information
'website' => ['type' => 'keyword'],
'phone' => ['type' => 'keyword'],
'client_hash' => ['type' => 'keyword'],
'routing_id' => ['type' => 'keyword'],
'vat_number' => ['type' => 'keyword'],
'id_number' => ['type' => 'keyword'],
'classification' => ['type' => 'keyword'],
// Financial information
'balance' => ['type' => 'float'],
'paid_to_date' => ['type' => 'float'],
'credit_balance' => ['type' => 'float'],
'payment_balance' => ['type' => 'float'],
// Address information
'address1' => [
'type' => 'text',
'analyzer' => 'standard'
],
'address2' => [
'type' => 'text',
'analyzer' => 'standard'
],
'city' => ['type' => 'keyword'],
'state' => ['type' => 'keyword'],
'postal_code' => ['type' => 'keyword'],
'country_id' => ['type' => 'keyword'],
// Shipping address
'shipping_address1' => [
'type' => 'text',
'analyzer' => 'standard'
],
'shipping_address2' => [
'type' => 'text',
'analyzer' => 'standard'
],
'shipping_city' => ['type' => 'keyword'],
'shipping_state' => ['type' => 'keyword'],
'shipping_postal_code' => ['type' => 'keyword'],
'shipping_country_id' => ['type' => 'keyword'],
// Classification and industry
'industry_id' => ['type' => 'keyword'],
'size_id' => ['type' => 'keyword'],
'group_settings_id' => ['type' => 'keyword'],
// Custom fields
'custom_value1' => [
'type' => 'text',
'analyzer' => 'standard'
],
'custom_value2' => [
'type' => 'text',
'analyzer' => 'standard'
],
'custom_value3' => [
'type' => 'text',
'analyzer' => 'standard'
],
'custom_value4' => [
'type' => 'text',
'analyzer' => 'standard'
],
// Notes and content
'private_notes' => [
'type' => 'text',
'analyzer' => 'standard'
],
'public_notes' => [
'type' => 'text',
'analyzer' => 'standard'
],
'display_name' => [
'type' => 'text',
'analyzer' => 'standard'
],
// Tax and invoice settings
'is_tax_exempt' => ['type' => 'boolean'],
'has_valid_vat_number' => ['type' => 'boolean'],
'tax_info' => ['type' => 'object'],
'e_invoice' => ['type' => 'object'],
// Settings and configuration
'settings' => ['type' => 'object'],
'sync' => ['type' => 'object'],
// Timestamps
'last_login' => ['type' => 'date'],
'created_at' => ['type' => 'date'],
'updated_at' => ['type' => 'date'],
'archived_at' => ['type' => 'date'],
// Company key for multi-tenancy
'company_key' => ['type' => 'keyword'],
]
];
Index::createRaw('clients_v2', $mapping);
}
/**
* Reverse the migration.
*/
public function down(): void
{
Index::dropIfExists('clients_v2');
}
}

20
lang/af_ZA/auth.php Normal file
View File

@ -0,0 +1,20 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'failed' => 'Hierdie geloofsbriewe stem nie ooreen met ons rekords nie.',
'password' => 'Die verskafde wagwoord is verkeerd.',
'throttle' => 'Te veel aanmeldpogings. Probeer asseblief weer oor :seconds sekondes.',
];

13
lang/af_ZA/help.php Normal file
View File

@ -0,0 +1,13 @@
<?php
$lang = array(
'client_dashboard' => 'Boodskap wat op kliënt se dashboard vertoon word',
'client_currency' => 'Die kliënt se geldeenheid.',
'client_language' => 'Die kliënt se taal.',
'client_payment_terms' => 'Die kliënt se betalingsvoorwaardes.',
'client_paid_invoice' => 'Boodskap wat op \'n kliënt se betaalde faktuur skerm vertoon word',
'client_unpaid_invoice' => 'Boodskap wat op \'n kliënt se onbetaalde faktuur skerm vertoon word',
'client_unapproved_quote' => 'Boodskap wat op \'n kliënt se nie-goedgekeurde kwotasie skerm vertoon word',
);
return $lang;

19
lang/af_ZA/pagination.php Normal file
View File

@ -0,0 +1,19 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application.
|
*/
'previous' => '« Vorige',
'next' => 'Volgende »',
];

23
lang/af_ZA/passwords.php Normal file
View File

@ -0,0 +1,23 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Password Reset Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password.
|
*/
'password' => 'Wagwoorde moet ten minste ses karakters wees en ooreenstem met die bevestiging.',
'reset' => 'Jou wagwoord is teruggestel!',
'sent' => 'Ons het jou wagwoord herstel skakel per e-pos gestuur!',
'token' => 'Hierdie wagwoord herstel token is ongeldig.',
'user' => 'Ons kan nie \'n gebruiker met daardie e-pos adres vind nie.',
'throttled' => 'Jy het onlangs wagwoord herstel aangevra, kyk asseblief in jou e-pos.',
];

5627
lang/af_ZA/texts.php Normal file

File diff suppressed because it is too large Load Diff

28
lang/af_ZA/ubl.php Normal file
View File

@ -0,0 +1,28 @@
<?php
$lang = array(
'free_export_item' => 'Free export item',
'outside_tax_scope' => 'Outside tax scope',
'eea_goods_and_services' => 'EEA goods and services',
'lower_rate' => 'Lower rate',
'mixed_tax_rate' => 'Mixed tax rate',
'higher_rate' => 'Higher rate',
'canary_islands_indirect_tax' => 'Canary Islands indirect tax',
'ceuta_and_melilla' => 'Ceuta and Melilla',
'transferred_vat_italy' => 'Transferred VAT Italy',
'exempt_for_resale' => 'Exempt for resale',
'vat_not_now_due' => 'VAT not now due',
'vat_due_previous_invoice' => 'VAT due previous',
'transferred_vat' => 'Transferred VAT',
'duty_paid_by_supplier' => 'Duty paid by supplier',
'vat_margin_scheme_travel_agents' => 'VAT margin scheme travel agents',
'vat_margin_scheme_second_hand_goods' => 'VAT margin scheme second hand goods',
'vat_margin_scheme_works_of_art' => 'VAT margin scheme works of art',
'vat_margin_scheme_collectors_items' => 'VAT margin scheme collectors items',
'vat_exempt_eea_intra_community' => 'VAT exempt EEA intra community',
'canary_islands_tax' => 'Canary Islands tax',
'tax_ceuta_melilla' => 'Tax Ceuta Melilla',
'services_outside_scope' => 'Services outside scope',
);
return $lang;

170
lang/af_ZA/validation.php Normal file
View File

@ -0,0 +1,170 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
'accepted' => 'The :attribute must be accepted.',
'accepted_if' => 'The :attribute must be accepted when :other is :value.',
'active_url' => 'The :attribute is not a valid URL.',
'after' => 'The :attribute must be a date after :date.',
'after_or_equal' => 'The :attribute must be a date after or equal to :date.',
'alpha' => 'The :attribute must only contain letters.',
'alpha_dash' => 'The :attribute must only contain letters, numbers, dashes and underscores.',
'alpha_num' => 'The :attribute must only contain letters and numbers.',
'array' => 'The :attribute must be an array.',
'before' => 'The :attribute must be a date before :date.',
'before_or_equal' => 'The :attribute must be a date before or equal to :date.',
'between' => [
'array' => 'The :attribute must have between :min and :max items.',
'file' => 'The :attribute must be between :min and :max kilobytes.',
'numeric' => 'The :attribute must be between :min and :max.',
'string' => 'The :attribute must be between :min and :max characters.',
],
'boolean' => 'The :attribute field must be true or false.',
'confirmed' => 'The :attribute confirmation does not match.',
'current_password' => 'The password is incorrect.',
'date' => 'The :attribute is not a valid date.',
'date_equals' => 'The :attribute must be a date equal to :date.',
'date_format' => 'The :attribute does not match the format :format.',
'declined' => 'The :attribute must be declined.',
'declined_if' => 'The :attribute must be declined when :other is :value.',
'different' => 'The :attribute and :other must be different.',
'digits' => 'The :attribute must be :digits digits.',
'digits_between' => 'The :attribute must be between :min and :max digits.',
'dimensions' => 'The :attribute has invalid image dimensions.',
'distinct' => 'The :attribute field has a duplicate value.',
'email' => 'The :attribute must be a valid email address.',
'ends_with' => 'The :attribute must end with one of the following: :values.',
'enum' => 'The selected :attribute is invalid.',
'exists' => 'The selected :attribute is invalid.',
'file' => 'The :attribute must be a file.',
'filled' => 'The :attribute field must have a value.',
'gt' => [
'array' => 'The :attribute must have more than :value items.',
'file' => 'The :attribute must be greater than :value kilobytes.',
'numeric' => 'The :attribute must be greater than :value.',
'string' => 'The :attribute must be greater than :value characters.',
],
'gte' => [
'array' => 'The :attribute must have :value items or more.',
'file' => 'The :attribute must be greater than or equal to :value kilobytes.',
'numeric' => 'The :attribute must be greater than or equal to :value.',
'string' => 'The :attribute must be greater than or equal to :value characters.',
],
'image' => 'The :attribute must be an image.',
'in' => 'The selected :attribute is invalid.',
'in_array' => 'The :attribute field does not exist in :other.',
'integer' => 'The :attribute must be an integer.',
'ip' => 'The :attribute must be a valid IP address.',
'ipv4' => 'The :attribute must be a valid IPv4 address.',
'ipv6' => 'The :attribute must be a valid IPv6 address.',
'json' => 'The :attribute must be a valid JSON string.',
'lt' => [
'array' => 'The :attribute must have less than :value items.',
'file' => 'The :attribute must be less than :value kilobytes.',
'numeric' => 'The :attribute must be less than :value.',
'string' => 'The :attribute must be less than :value characters.',
],
'lte' => [
'array' => 'The :attribute must not have more than :value items.',
'file' => 'The :attribute must be less than or equal to :value kilobytes.',
'numeric' => 'The :attribute must be less than or equal to :value.',
'string' => 'The :attribute must be less than or equal to :value characters.',
],
'mac_address' => 'The :attribute must be a valid MAC address.',
'max' => [
'array' => 'The :attribute must not have more than :max items.',
'file' => 'The :attribute must not be greater than :max kilobytes.',
'numeric' => 'The :attribute must not be greater than :max.',
'string' => 'The :attribute must not be greater than :max characters.',
],
'mimes' => 'The :attribute must be a file of type: :values.',
'mimetypes' => 'The :attribute must be a file of type: :values.',
'min' => [
'array' => 'The :attribute must have at least :min items.',
'file' => 'The :attribute must be at least :min kilobytes.',
'numeric' => 'The :attribute must be at least :min.',
'string' => 'The :attribute must be at least :min characters.',
],
'multiple_of' => 'The :attribute must be a multiple of :value.',
'not_in' => 'The selected :attribute is invalid.',
'not_regex' => 'The :attribute format is invalid.',
'numeric' => 'The :attribute must be a number.',
'password' => [
'letters' => 'The :attribute must contain at least one letter.',
'mixed' => 'The :attribute must contain at least one uppercase and one lowercase letter.',
'numbers' => 'The :attribute must contain at least one number.',
'symbols' => 'The :attribute must contain at least one symbol.',
'uncompromised' => 'The given :attribute has appeared in a data leak. Please choose a different :attribute.',
],
'present' => 'The :attribute field must be present.',
'prohibited' => 'The :attribute field is prohibited.',
'prohibited_if' => 'The :attribute field is prohibited when :other is :value.',
'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.',
'prohibits' => 'The :attribute field prohibits :other from being present.',
'regex' => 'The :attribute format is invalid.',
'required' => 'The :attribute field is required.',
'required_array_keys' => 'The :attribute field must contain entries for: :values.',
'required_if' => 'The :attribute field is required when :other is :value.',
'required_unless' => 'The :attribute field is required unless :other is in :values.',
'required_with' => 'The :attribute field is required when :values is present.',
'required_with_all' => 'The :attribute field is required when :values are present.',
'required_without' => 'The :attribute field is required when :values is not present.',
'required_without_all' => 'The :attribute field is required when none of :values are present.',
'same' => 'The :attribute and :other must match.',
'size' => [
'array' => 'The :attribute must contain :size items.',
'file' => 'The :attribute must be :size kilobytes.',
'numeric' => 'The :attribute must be :size.',
'string' => 'The :attribute must be :size characters.',
],
'starts_with' => 'The :attribute must start with one of the following: :values.',
'doesnt_start_with' => 'The :attribute may not start with one of the following: :values.',
'string' => 'The :attribute must be a string.',
'timezone' => 'The :attribute must be a valid timezone.',
'unique' => 'The :attribute has already been taken.',
'uploaded' => 'The :attribute failed to upload.',
'url' => 'The :attribute must be a valid URL.',
'uuid' => 'The :attribute must be a valid UUID.',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap our attribute placeholder
| with something more reader friendly such as "E-Mail Address" instead
| of "email". This simply helps us make our message more expressive.
|
*/
'attributes' => [],
];

View File

@ -5596,6 +5596,13 @@ $lang = array(
'tax_nexus' => 'الرابط الضريبي',
'tax_period_report' => 'تقرير الفترة الضريبية',
'creator' => 'تم الإنشاء بواسطة',
'ses_topic_arn_help' => 'موضوع SES (اختياري، لتتبع خطاف الويب فقط)',
'ses_region_help' => 'منطقة AWS، أي us-east-1',
'ses_secret_key' => 'مفتاح SES السري',
'ses_access_key' => 'معرف مفتاح الوصول SES',
'activity_151' => 'تم دمج العميل :notes في :client بواسطة :user',
'activity_152' => 'تم دمج البائع :notes في :vendor بواسطة :user',
'activity_153' => 'تم تطهير العميل :notes بواسطة :user',
);
return $lang;

View File

@ -5615,6 +5615,13 @@ $lang = array(
'tax_nexus' => 'Nexus fiscal',
'tax_period_report' => 'Informe del període fiscal',
'creator' => 'Creat per',
'ses_topic_arn_help' => 'El tema SES (opcional, només per al seguiment de webhooks)',
'ses_region_help' => 'La regió d&#39;AWS, és a dir, us-east-1',
'ses_secret_key' => 'Clau secreta SES',
'ses_access_key' => 'ID de la clau d&#39;accés SES',
'activity_151' => 'El client :notes s&#39;ha fusionat amb :client per :user',
'activity_152' => 'El proveïdor :notes s&#39;ha fusionat amb :vendor per :user',
'activity_153' => 'Client :notes purgat per :user',
);
return $lang;

View File

@ -5622,6 +5622,15 @@ $lang = array(
'activity_151' => 'Client :notes merged into :client by :user',
'activity_152' => 'Vendor :notes merged into :vendor by :user',
'activity_153' => 'Client :notes purged by :user',
'lifecycle' => 'Lifecycle',
'order_columns' => 'Order Columns',
'topic_arn' => 'Topic ARN',
'lang_Catalan' => 'Catalan',
'lang_Afrikaans' => 'Afrikaans',
'lang_Indonesian' => 'Indonesian',
'replaced' => 'Replaced',
'ses_from_address' => 'SES From Address',
'ses_from_address_help' => 'The Sending Email Address, must be verified in AWS',
);
return $lang;

View File

@ -5614,6 +5614,13 @@ $lang = array(
'tax_nexus' => 'Nexo fiscal',
'tax_period_report' => 'Informe del período impositivo',
'creator' => 'Creado por',
'ses_topic_arn_help' => 'El tema SES (opcional, solo para seguimiento de webhooks)',
'ses_region_help' => 'La región de AWS, es decir, us-east-1',
'ses_secret_key' => 'Clave secreta de SES',
'ses_access_key' => 'ID de clave de acceso de SES',
'activity_151' => 'El cliente :notes se fusionó con :client por :user',
'activity_152' => 'El proveedor :notes se fusionó con :vendor por :user',
'activity_153' => 'Cliente :notes purgado por :user',
);
return $lang;

View File

@ -5615,6 +5615,13 @@ Lorsque les montant apparaîtront sur votre relevé, veuillez revenir sur cette
'tax_nexus' => 'Lien fiscal',
'tax_period_report' => 'Rapport de période fiscale',
'creator' => 'Créé par',
'ses_topic_arn_help' => 'Sujet SES (facultatif, uniquement pour le suivi des webhooks)',
'ses_region_help' => 'La région AWS, ex. us-east-1',
'ses_secret_key' => 'Clé secrète SES',
'ses_access_key' => 'ID de la clé d\'accès SES',
'activity_151' => 'Client :notes fusionné avec :client par :user',
'activity_152' => 'Le fournisseur :notes a été fusionné avec :vendor par :user',
'activity_153' => 'Client :notes purgé par :user',
);
return $lang;

20
lang/id_ID/auth.php Normal file
View File

@ -0,0 +1,20 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'failed' => 'Kredensial ini tidak cocok dengan catatan kami.', // These credentials do not match our records
'password' => 'Kata sandi yang diberikan tidak benar.', // The provided password is incorrect
'throttle' => 'Terlalu banyak percobaan login. Silakan coba lagi dalam :seconds detik.', // Too many login attempts. Please try again in :seconds seconds
];

13
lang/id_ID/help.php Normal file
View File

@ -0,0 +1,13 @@
<?php
$lang = array(
'client_dashboard' => 'Pesan yang akan ditampilkan di dashboard klien', // Message to be displayed on clients dashboard
'client_currency' => 'Mata uang klien.', // The client currency
'client_language' => 'Bahasa klien.', // The client language
'client_payment_terms' => 'Syarat pembayaran klien.', // The client payment terms
'client_paid_invoice' => 'Pesan yang akan ditampilkan di layar faktur berbayar klien', // Message to be displayed on a clients paid invoice screen
'client_unpaid_invoice' => 'Pesan yang akan ditampilkan di layar faktur belum dibayar klien', // Message to be displayed on a clients unpaid invoice screen
'client_unapproved_quote' => 'Pesan yang akan ditampilkan di layar penawaran belum disetujui klien', // Message to be displayed on a clients unapproved quote screen
);
return $lang;

19
lang/id_ID/pagination.php Normal file
View File

@ -0,0 +1,19 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application.
|
*/
'previous' => '« Sebelumnya', // Previous
'next' => 'Selanjutnya »', // Next
];

23
lang/id_ID/passwords.php Normal file
View File

@ -0,0 +1,23 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Password Reset Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| has failed, such as for an invalid token or invalid new password.
|
*/
'password' => 'Kata sandi harus minimal enam karakter dan cocok dengan konfirmasi.', // Passwords must be at least six characters and match the confirmation
'reset' => 'Kata sandi Anda telah diatur ulang!', // Your password has been reset
'sent' => 'Kami telah mengirim email link reset kata sandi Anda!', // We have e-mailed your password reset link
'token' => 'Token reset kata sandi ini tidak valid.', // This password reset token is invalid
'user' => "Kami tidak dapat menemukan pengguna dengan alamat email tersebut.", // We can't find a user with that e-mail address
'throttled' => 'Anda telah meminta reset kata sandi baru-baru ini, silakan periksa email Anda.', // You have requested password reset recently, please check your email
];

5620
lang/id_ID/texts.php Normal file

File diff suppressed because it is too large Load Diff

28
lang/id_ID/ubl.php Normal file
View File

@ -0,0 +1,28 @@
<?php
$lang = array(
'free_export_item' => 'Item ekspor gratis', // Free export item
'outside_tax_scope' => 'Di luar cakupan pajak', // Outside tax scope
'eea_goods_and_services' => 'Barang dan jasa EEA', // EEA goods and services
'lower_rate' => 'Tarif lebih rendah', // Lower rate
'mixed_tax_rate' => 'Tarif pajak campuran', // Mixed tax rate
'higher_rate' => 'Tarif lebih tinggi', // Higher rate
'canary_islands_indirect_tax' => 'Pajak tidak langsung Kepulauan Canary', // Canary Islands indirect tax
'ceuta_and_melilla' => 'Ceuta dan Melilla', // Ceuta and Melilla
'transferred_vat_italy' => 'PPN yang ditransfer Italia', // Transferred VAT Italy
'exempt_for_resale' => 'Bebas untuk dijual kembali', // Exempt for resale
'vat_not_now_due' => 'PPN tidak jatuh tempo sekarang', // VAT not now due
'vat_due_previous_invoice' => 'PPN jatuh tempo sebelumnya', // VAT due previous
'transferred_vat' => 'PPN yang ditransfer', // Transferred VAT
'duty_paid_by_supplier' => 'Bea dibayar oleh pemasok', // Duty paid by supplier
'vat_margin_scheme_travel_agents' => 'Skema margin PPN agen perjalanan', // VAT margin scheme travel agents
'vat_margin_scheme_second_hand_goods' => 'Skema margin PPN barang bekas', // VAT margin scheme second hand goods
'vat_margin_scheme_works_of_art' => 'Skema margin PPN karya seni', // VAT margin scheme works of art
'vat_margin_scheme_collectors_items' => 'Skema margin PPN item kolektor', // VAT margin scheme collectors items
'vat_exempt_eea_intra_community' => 'PPN bebas EEA intra komunitas', // VAT exempt EEA intra community
'canary_islands_tax' => 'Pajak Kepulauan Canary', // Canary Islands tax
'tax_ceuta_melilla' => 'Pajak Ceuta Melilla', // Tax Ceuta Melilla
'services_outside_scope' => 'Layanan di luar cakupan', // Services outside scope
);
return $lang;

170
lang/id_ID/validation.php Normal file
View File

@ -0,0 +1,170 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
'accepted' => 'The :attribute harus diterima.', // The :attribute must be accepted
'accepted_if' => 'The :attribute harus diterima ketika :other adalah :value.', // The :attribute must be accepted when :other is :value
'active_url' => 'The :attribute bukan URL yang valid.', // The :attribute is not a valid URL
'after' => 'The :attribute harus berupa tanggal setelah :date.', // The :attribute must be a date after :date
'after_or_equal' => 'The :attribute harus berupa tanggal setelah atau sama dengan :date.', // The :attribute must be a date after or equal to :date
'alpha' => 'The :attribute hanya boleh berisi huruf.', // The :attribute must only contain letters
'alpha_dash' => 'The :attribute hanya boleh berisi huruf, angka, tanda hubung dan garis bawah.', // The :attribute must only contain letters, numbers, dashes and underscores
'alpha_num' => 'The :attribute hanya boleh berisi huruf dan angka.', // The :attribute must only contain letters and numbers
'array' => 'The :attribute harus berupa array.', // The :attribute must be an array
'before' => 'The :attribute harus berupa tanggal sebelum :date.', // The :attribute must be a date before :date
'before_or_equal' => 'The :attribute harus berupa tanggal sebelum atau sama dengan :date.', // The :attribute must be a date before or equal to :date
'between' => [
'array' => 'The :attribute harus memiliki antara :min dan :max item.', // The :attribute must have between :min and :max items
'file' => 'The :attribute harus antara :min dan :max kilobyte.', // The :attribute must be between :min and :max kilobytes
'numeric' => 'The :attribute harus antara :min dan :max.', // The :attribute must be between :min and :max
'string' => 'The :attribute harus antara :min dan :max karakter.', // The :attribute must be between :min and :max characters
],
'boolean' => 'Bidang :attribute harus benar atau salah.', // The :attribute field must be true or false
'confirmed' => 'Konfirmasi :attribute tidak cocok.', // The :attribute confirmation does not match
'current_password' => 'Kata sandi salah.', // The password is incorrect
'date' => 'The :attribute bukan tanggal yang valid.', // The :attribute is not a valid date
'date_equals' => 'The :attribute harus berupa tanggal yang sama dengan :date.', // The :attribute must be a date equal to :date
'date_format' => 'The :attribute tidak cocok dengan format :format.', // The :attribute does not match the format :format
'declined' => 'The :attribute harus ditolak.', // The :attribute must be declined
'declined_if' => 'The :attribute harus ditolak ketika :other adalah :value.', // The :attribute must be declined when :other is :value
'different' => 'The :attribute dan :other harus berbeda.', // The :attribute and :other must be different
'digits' => 'The :attribute harus :digits digit.', // The :attribute must be :digits digits
'digits_between' => 'The :attribute harus antara :min dan :max digit.', // The :attribute must be between :min and :max digits
'dimensions' => 'The :attribute memiliki dimensi gambar yang tidak valid.', // The :attribute has invalid image dimensions
'distinct' => 'Bidang :attribute memiliki nilai duplikat.', // The :attribute field has a duplicate value
'email' => 'The :attribute harus berupa alamat email yang valid.', // The :attribute must be a valid email address
'ends_with' => 'The :attribute harus diakhiri dengan salah satu dari: :values.', // The :attribute must end with one of the following: :values
'enum' => 'The :attribute yang dipilih tidak valid.', // The selected :attribute is invalid
'exists' => 'The :attribute yang dipilih tidak valid.', // The selected :attribute is invalid
'file' => 'The :attribute harus berupa file.', // The :attribute must be a file
'filled' => 'Bidang :attribute harus memiliki nilai.', // The :attribute field must have a value
'gt' => [
'array' => 'The :attribute harus memiliki lebih dari :value item.', // The :attribute must have more than :value items
'file' => 'The :attribute harus lebih besar dari :value kilobyte.', // The :attribute must be greater than :value kilobytes
'numeric' => 'The :attribute harus lebih besar dari :value.', // The :attribute must be greater than :value
'string' => 'The :attribute harus lebih besar dari :value karakter.', // The :attribute must be greater than :value characters
],
'gte' => [
'array' => 'The :attribute harus memiliki :value item atau lebih.', // The :attribute must have :value items or more
'file' => 'The :attribute harus lebih besar dari atau sama dengan :value kilobyte.', // The :attribute must be greater than or equal to :value kilobytes
'numeric' => 'The :attribute harus lebih besar dari atau sama dengan :value.', // The :attribute must be greater than or equal to :value
'string' => 'The :attribute harus lebih besar dari atau sama dengan :value karakter.', // The :attribute must be greater than or equal to :value characters
],
'image' => 'The :attribute harus berupa gambar.', // The :attribute must be an image
'in' => 'The :attribute yang dipilih tidak valid.', // The selected :attribute is invalid
'in_array' => 'Bidang :attribute tidak ada dalam :other.', // The :attribute field does not exist in :other
'integer' => 'The :attribute harus berupa integer.', // The :attribute must be an integer
'ip' => 'The :attribute harus berupa alamat IP yang valid.', // The :attribute must be a valid IP address
'ipv4' => 'The :attribute harus berupa alamat IPv4 yang valid.', // The :attribute must be a valid IPv4 address
'ipv6' => 'The :attribute harus berupa alamat IPv6 yang valid.', // The :attribute must be a valid IPv6 address
'json' => 'The :attribute harus berupa string JSON yang valid.', // The :attribute must be a valid JSON string
'lt' => [
'array' => 'The :attribute harus memiliki kurang dari :value item.', // The :attribute must have less than :value items
'file' => 'The :attribute harus kurang dari :value kilobyte.', // The :attribute must be less than :value kilobytes
'numeric' => 'The :attribute harus kurang dari :value.', // The :attribute must be less than :value
'string' => 'The :attribute harus kurang dari :value karakter.', // The :attribute must be less than :value characters
],
'lte' => [
'array' => 'The :attribute tidak boleh memiliki lebih dari :value item.', // The :attribute must not have more than :value items
'file' => 'The :attribute harus kurang dari atau sama dengan :value kilobyte.', // The :attribute must be less than or equal to :value kilobytes
'numeric' => 'The :attribute harus kurang dari atau sama dengan :value.', // The :attribute must be less than or equal to :value
'string' => 'The :attribute harus kurang dari atau sama dengan :value karakter.', // The :attribute must be less than or equal to :value characters
],
'mac_address' => 'The :attribute harus berupa alamat MAC yang valid.', // The :attribute must be a valid MAC address
'max' => [
'array' => 'The :attribute tidak boleh memiliki lebih dari :max item.', // The :attribute must not have more than :max items
'file' => 'The :attribute tidak boleh lebih besar dari :max kilobyte.', // The :attribute must not be greater than :max kilobytes
'numeric' => 'The :attribute tidak boleh lebih besar dari :max.', // The :attribute must not be greater than :max
'string' => 'The :attribute tidak boleh lebih besar dari :max karakter.', // The :attribute must not be greater than :max characters
],
'mimes' => 'The :attribute harus berupa file bertipe: :values.', // The :attribute must be a file of type: :values
'mimetypes' => 'The :attribute harus berupa file bertipe: :values.', // The :attribute must be a file of type: :values
'min' => [
'array' => 'The :attribute harus memiliki minimal :min item.', // The :attribute must have at least :min items
'file' => 'The :attribute harus minimal :min kilobyte.', // The :attribute must be at least :min kilobytes
'numeric' => 'The :attribute harus minimal :min.', // The :attribute must be at least :min
'string' => 'The :attribute harus minimal :min karakter.', // The :attribute must be at least :min characters
],
'multiple_of' => 'The :attribute harus kelipatan dari :value.', // The :attribute must be a multiple of :value
'not_in' => 'The :attribute yang dipilih tidak valid.', // The selected :attribute is invalid
'not_regex' => 'Format :attribute tidak valid.', // The :attribute format is invalid
'numeric' => 'The :attribute harus berupa angka.', // The :attribute must be a number
'password' => [
'letters' => 'The :attribute harus mengandung minimal satu huruf.', // The :attribute must contain at least one letter
'mixed' => 'The :attribute harus mengandung minimal satu huruf besar dan satu huruf kecil.', // The :attribute must contain at least one uppercase and one lowercase letter
'numbers' => 'The :attribute harus mengandung minimal satu angka.', // The :attribute must contain at least one number
'symbols' => 'The :attribute harus mengandung minimal satu simbol.', // The :attribute must contain at least one symbol
'uncompromised' => 'The :attribute yang diberikan telah muncul dalam kebocoran data. Silakan pilih :attribute yang berbeda.', // The given :attribute has appeared in a data leak. Please choose a different :attribute
],
'present' => 'Bidang :attribute harus ada.', // The :attribute field must be present
'prohibited' => 'Bidang :attribute dilarang.', // The :attribute field is prohibited
'prohibited_if' => 'Bidang :attribute dilarang ketika :other adalah :value.', // The :attribute field is prohibited when :other is :value
'prohibited_unless' => 'Bidang :attribute dilarang kecuali :other ada dalam :values.', // The :attribute field is prohibited unless :other is in :values
'prohibits' => 'Bidang :attribute melarang :other untuk ada.', // The :attribute field prohibits :other from being present
'regex' => 'Format :attribute tidak valid.', // The :attribute format is invalid
'required' => 'Bidang :attribute wajib diisi.', // The :attribute field is required
'required_array_keys' => 'Bidang :attribute harus berisi entri untuk: :values.', // The :attribute field must contain entries for: :values
'required_if' => 'Bidang :attribute wajib diisi ketika :other adalah :value.', // The :attribute field is required when :other is :value
'required_unless' => 'Bidang :attribute wajib diisi kecuali :other ada dalam :values.', // The :attribute field is required unless :other is in :values
'required_with' => 'Bidang :attribute wajib diisi ketika :values ada.', // The :attribute field is required when :values is present
'required_with_all' => 'Bidang :attribute wajib diisi ketika :values ada.', // The :attribute field is required when :values are present
'required_without' => 'Bidang :attribute wajib diisi ketika :values tidak ada.', // The :attribute field is required when :values is not present
'required_without_all' => 'Bidang :attribute wajib diisi ketika tidak ada :values yang ada.', // The :attribute field is required when none of :values are present
'same' => 'The :attribute dan :other harus cocok.', // The :attribute and :other must match
'size' => [
'array' => 'The :attribute harus berisi :size item.', // The :attribute must contain :size items
'file' => 'The :attribute harus :size kilobyte.', // The :attribute must be :size kilobytes
'numeric' => 'The :attribute harus :size.', // The :attribute must be :size
'string' => 'The :attribute harus :size karakter.', // The :attribute must be :size characters
],
'starts_with' => 'The :attribute harus dimulai dengan salah satu dari: :values.', // The :attribute must start with one of the following: :values
'doesnt_start_with' => 'The :attribute tidak boleh dimulai dengan salah satu dari: :values.', // The :attribute may not start with one of the following: :values
'string' => 'The :attribute harus berupa string.', // The :attribute must be a string
'timezone' => 'The :attribute harus berupa timezone yang valid.', // The :attribute must be a valid timezone
'unique' => 'The :attribute sudah diambil.', // The :attribute has already been taken
'uploaded' => 'The :attribute gagal diunggah.', // The :attribute failed to upload
'url' => 'The :attribute harus berupa URL yang valid.', // The :attribute must be a valid URL
'uuid' => 'The :attribute harus berupa UUID yang valid.', // The :attribute must be a valid UUID
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap our attribute placeholder
| with something more reader friendly such as "E-Mail Address" instead
| of "email". This simply helps us make our message more expressive.
|
*/
'attributes' => [],
];

View File

@ -619,7 +619,7 @@ class ClientTest extends TestCase
$data = [
'name' => 'A loyal Client',
'contacts' => $this->faker->unique()->safeEmail(),
'contacts' => \Illuminate\Support\Str::random(32)."@example.com",
];
try {
@ -674,7 +674,7 @@ class ClientTest extends TestCase
$data = [
'name' => 'A loyal Client',
'contacts' => [
['email' => $this->faker->unique()->safeEmail()],
['email' => \Illuminate\Support\Str::random(32)."@example.com"],
],
];
@ -690,7 +690,7 @@ class ClientTest extends TestCase
'name' => 'A loyal Client',
'contacts' => [
[
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
'password' => '*****',
],
],
@ -706,7 +706,7 @@ class ClientTest extends TestCase
'name' => 'A loyal Client',
'contacts' => [
[
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
'password' => '1',
],
],
@ -728,7 +728,7 @@ class ClientTest extends TestCase
'name' => 'A loyal Client',
'contacts' => [
[
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
'password' => '1Qajsj...33',
],
],
@ -751,11 +751,11 @@ class ClientTest extends TestCase
'name' => 'A loyal Client',
'contacts' => [
[
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
'password' => '1Qajsj...33',
],
[
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
'password' => '1234AAAAAaaaaa',
],
],
@ -786,7 +786,7 @@ class ClientTest extends TestCase
$arr = $response->json();
$safe_email = $this->faker->unique()->safeEmail();
$safe_email = \Illuminate\Support\Str::random(32)."@example.com";
$data = [
'name' => 'A loyal Client',
@ -820,7 +820,7 @@ class ClientTest extends TestCase
$this->assertEquals(0, strlen($contact->password));
$safe_email = $this->faker->unique()->safeEmail();
$safe_email = \Illuminate\Support\Str::random(32)."@example.com";
$data = [
'name' => 'A loyal Client',

View File

@ -113,7 +113,7 @@ class InvoicePeriodTest extends TestCase
$arr = $response->json();
nlog($arr);
// nlog($arr);
$response->assertStatus(422);

View File

@ -88,7 +88,7 @@ class ArDetailReportTest extends TestCase
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
$settings = CompanySettings::defaults();

View File

@ -85,7 +85,7 @@ class ArSummaryReportTest extends TestCase
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
$settings = CompanySettings::defaults();

View File

@ -85,7 +85,7 @@ class ClientBalanceReportTest extends TestCase
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
$settings = CompanySettings::defaults();

View File

@ -85,7 +85,7 @@ class ClientSalesReportTest extends TestCase
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
$settings = CompanySettings::defaults();

View File

@ -91,7 +91,7 @@ class EInvoiceReportTest extends TestCase
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32).'@example.com',
]);
$settings = CompanySettings::defaults();

View File

@ -89,7 +89,7 @@ class ProductSalesReportTest extends TestCase
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
$settings = CompanySettings::defaults();

View File

@ -88,7 +88,7 @@ class ProfitAndLossReportTest extends TestCase
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
$settings = CompanySettings::defaults();

View File

@ -207,7 +207,7 @@ class ReportCsvGenerationTest extends TestCase
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
$settings = CompanySettings::defaults();
@ -1181,13 +1181,12 @@ $this->account->forceDelete();
config(['queue.default' => 'redis']);
Credit::factory()->create([
Credit::factory()->count(100)->create([
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'client_id' => $this->client->id,
'amount' => 100,
'balance' => 50,
'number' => '1234',
'status_id' => 2,
'discount' => 10,
'po_number' => '1234',

View File

@ -88,7 +88,7 @@ class TaxSummaryReportTest extends TestCase
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32).'@example.com',
]);
$settings = CompanySettings::defaults();

View File

@ -88,7 +88,7 @@ class UserSalesReportTest extends TestCase
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
$settings = CompanySettings::defaults();

View File

@ -100,8 +100,8 @@ class QuickbooksMappingTest extends TestCase
Invoice::where('company_id', $this->company->id)->cursor()->each(function ($invoice) use ($qb_invoices) {
$qb_invoice = $qb_invoices->where('Id', $invoice->sync->qb_id)->first();
nlog($qb_invoice);
nlog($invoice->toArray());
// nlog($qb_invoice);
// nlog($invoice->toArray());
if(!$qb_invoice) {
nlog("Borked trying to find invoice {$invoice->sync->qb_id} in qb_invoices");
}
@ -111,10 +111,10 @@ class QuickbooksMappingTest extends TestCase
$total_amount = $qb_invoice['TotalAmt'];
$total_balance = $qb_invoice['Balance'];
nlog($total_amount);
nlog($invoice->amount);
nlog($total_balance);
nlog($invoice->balance);
// nlog($total_amount);
// nlog($invoice->amount);
// nlog($total_balance);
// nlog($invoice->balance);
$delta_amount = abs(round($total_amount - $invoice->amount,2));
$delta_balance = abs(round($total_balance - $invoice->balance,2));

View File

@ -45,6 +45,9 @@ class InventoryManagementTest extends TestCase
public function testInventoryMovements()
{
config(['queue.default' => 'sync']);
$product = Product::factory()->create([
'user_id' => $this->user->id,
'company_id' => $this->company->id,

View File

@ -56,7 +56,7 @@ class MultiPaymentDeleteTest extends TestCase
$user = User::factory()->create([
'account_id' => $account->id,
'confirmation_code' => '11',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
$cu = CompanyUserFactory::create($user->id, $company->id, $account->id);

View File

@ -87,7 +87,7 @@ class QuoteReminderTest extends TestCase
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
if(!$settings) {

View File

@ -87,7 +87,7 @@ class ReminderTest extends TestCase
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
if(!$settings) {
@ -295,7 +295,7 @@ class ReminderTest extends TestCase
$user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
$settings = CompanySettings::defaults();

View File

@ -73,7 +73,7 @@ class UserTest extends TestCase
$user = User::factory()->create([
'account_id' => $account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
$user->password = \Illuminate\Support\Facades\Hash::make('ALongAndBriliantPassword');
@ -204,7 +204,7 @@ class UserTest extends TestCase
$data = $user->toArray();
$data['email'] = $this->faker->unique()->safeEmail();
$data['email'] = \Illuminate\Support\Str::random(32)."@example.com";
unset($data['password']);
$response = $this->withHeaders([
@ -357,7 +357,7 @@ class UserTest extends TestCase
$user = User::factory()->create([
'account_id' => $account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
'password' => \Illuminate\Support\Facades\Hash::make('ALongAndBriliantPassword'),
]);

View File

@ -162,7 +162,7 @@ class StorecoveIngestTest extends TestCase
$xslt = new XsltDocumentValidator($decoded);
$html = $xslt->getHtml();
nlog($html);
// nlog($html);
$this->assertIsString($html);
}

View File

@ -50,7 +50,7 @@ class StorecoveRouterTest extends TestCase
$user = User::factory()->create([
'account_id' => $account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
'password' => \Illuminate\Support\Facades\Hash::make('ALongAndBriliantPassword'),
]);

View File

@ -490,7 +490,7 @@ $this->assertTrue(in_array($item->tax_id, ['1','2']));
$arr = $this->removeEmptyValues($arr);
nlog($arr);
// nlog($arr);
}
@ -1018,7 +1018,7 @@ $this->assertTrue(in_array($item->tax_id, ['1','2']));
$settings->state = 'Lazio';
$settings->postal_code = '00187';
$settings->phone = '06 1234567';
$settings->email = $this->faker->unique()->safeEmail();
$settings->email = \Illuminate\Support\Str::random(32)."@example.com";
$settings->country_id = '380'; // Italy's ISO country code
$settings->vat_number = 'IT92443356490'; // Italian VAT number
$settings->id_number = 'RM 123456'; // Typical Italian company registration format
@ -1181,7 +1181,7 @@ $this->assertTrue(in_array($item->tax_id, ['1','2']));
$settings->state = 'Berlin';
$settings->postal_code = '10115';
$settings->phone = '030 1234567';
$settings->email = $this->faker->unique()->safeEmail();
$settings->email = \Illuminate\Support\Str::random(32)."@example.com";
$settings->country_id = '276'; // Germany's ISO country code
$settings->vat_number = 'DE123456789';
$settings->id_number = 'HRB 98765';
@ -1289,7 +1289,7 @@ $this->assertTrue(in_array($item->tax_id, ['1','2']));
$settings->state = 'Madrid';
$settings->postal_code = '28013';
$settings->phone = '030 1234567';
$settings->email = $this->faker->unique()->safeEmail();
$settings->email = \Illuminate\Support\Str::random(32)."@example.com";
$settings->country_id = '724'; // Germany's ISO country code
$settings->vat_number = 'ESB16645678';
$settings->id_number = 'HRB 12345';
@ -1397,7 +1397,7 @@ $this->assertTrue(in_array($item->tax_id, ['1','2']));
$settings->state = 'Île-de-France';
$settings->postal_code = '75002';
$settings->phone = '01 23456789';
$settings->email = $this->faker->unique()->safeEmail();
$settings->email = \Illuminate\Support\Str::random(32)."@example.com";
$settings->country_id = '250'; // France's ISO country code
$settings->vat_number = 'FR82345678911';
$settings->id_number = '12345678900010';
@ -1508,7 +1508,7 @@ $this->assertTrue(in_array($item->tax_id, ['1','2']));
$settings->state = 'Vienna';
$settings->postal_code = '1010';
$settings->phone = '+43 1 23456789';
$settings->email = $this->faker->unique()->safeEmail();
$settings->email = \Illuminate\Support\Str::random(32)."@example.com";
$settings->country_id = '40'; // Austria's ISO country code
$settings->vat_number = 'ATU92335648';
$settings->id_number = 'FN 123456x';
@ -1617,7 +1617,7 @@ $this->assertTrue(in_array($item->tax_id, ['1','2']));
$settings->state = 'Bucharest';
$settings->postal_code = '010101';
$settings->phone = '021 1234567';
$settings->email = $this->faker->unique()->safeEmail();
$settings->email = \Illuminate\Support\Str::random(32)."@example.com";
$settings->country_id = '642'; // Romania's ISO country code
$settings->vat_number = 'RO92443356490'; // Romanian VAT number format
$settings->id_number = 'B12345678'; // Typical Romanian company registration format
@ -1744,7 +1744,7 @@ $this->assertTrue(in_array($item->tax_id, ['1','2']));
$p->run();
$xml = $p->toXml();
nlog($xml);
// nlog($xml);
// $identifiers = $p->getStorecoveMeta();
@ -1779,7 +1779,7 @@ $this->assertTrue(in_array($item->tax_id, ['1','2']));
$p->run();
$xml = $p->toXml();
nlog($xml);
// nlog($xml);
$identifiers = $p->getStorecoveMeta();
@ -1809,7 +1809,7 @@ $this->assertTrue(in_array($item->tax_id, ['1','2']));
$p->run();
$xml = $p->toXml();
nlog($xml);
// nlog($xml);
$identifiers = $p->getStorecoveMeta();
@ -1819,7 +1819,7 @@ $this->assertTrue(in_array($item->tax_id, ['1','2']));
//test individual sending
nlog("Individual");
// nlog("Individual");
$invoice = $this->createITData(false);
@ -1837,7 +1837,7 @@ $this->assertTrue(in_array($item->tax_id, ['1','2']));
$p->run();
$xml = $p->toXml();
nlog($xml);
// nlog($xml);
$identifiers = $p->getStorecoveMeta();
@ -1870,7 +1870,7 @@ $this->assertTrue(in_array($item->tax_id, ['1','2']));
$p->run();
$xml = $p->toXml();
nlog($xml);
// nlog($xml);
$identifiers = $p->getStorecoveMeta();
@ -1901,7 +1901,7 @@ $this->assertTrue(in_array($item->tax_id, ['1','2']));
$p->run();
$xml = $p->toXml();
nlog($xml);
// nlog($xml);
$identifiers = $p->getStorecoveMeta();
@ -1932,7 +1932,7 @@ $this->assertTrue(in_array($item->tax_id, ['1','2']));
$p->run();
$xml = $p->toXml();
nlog($xml);
// nlog($xml);
$identifiers = [
"routing" => [
@ -1971,7 +1971,7 @@ $this->assertTrue(in_array($item->tax_id, ['1','2']));
$p->run();
$xml = $p->toXml();
nlog($xml);
// nlog($xml);
$identifiers = [
"routing" => [

View File

@ -707,6 +707,10 @@ class EventTest extends TestCase
{
$this->withoutMiddleware(PasswordProtection::class);
$u = \App\Models\User::where('email','bob1@good.ole.boys.com')->cursor()->each(function($user) {
$user->account->forceDelete();
});
Event::fake();
$data = [
@ -774,8 +778,6 @@ class EventTest extends TestCase
->assertStatus(200);
Event::assertDispatched(UserWasCreated::class);
Event::assertDispatched(UserWasUpdated::class);

View File

@ -56,7 +56,7 @@ trait MockUnitData
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
$this->company = Company::factory()->create([

View File

@ -68,7 +68,7 @@ class CheckDataTest extends TestCase
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
$settings = CompanySettings::defaults();
@ -182,12 +182,12 @@ class CheckDataTest extends TestCase
User::factory()->create([
'account_id' => $this->account->id,
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
User::factory()->create([
'account_id' => $this->account->id,
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
$user_hash = 'a';

View File

@ -79,7 +79,7 @@ class InvoiceMarkPaidTest extends TestCase
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
$settings = CompanySettings::defaults();

View File

@ -70,7 +70,7 @@ class LateFeeTest extends TestCase
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
'email' => \Illuminate\Support\Str::random(32)."@example.com",
]);
$this->company = Company::factory()->create([
@ -271,9 +271,9 @@ class LateFeeTest extends TestCase
public function testLateFeeRemovals()
{
if(!config('ninja.testvars.stripe')){
// if(!config('ninja.testvars.stripe')){
$this->markTestSkipped('Stripe is not enabled');
}
// }
config(['queue.default' => 'sync']);

View File

@ -43,7 +43,7 @@ class RecurringExpenseCloneTest extends TestCase
public function testBadBase64String()
{
$account = Account::factory()->create();
$user = User::factory()->create(['account_id' => $account->id, 'email' => $this->faker->unique()->safeEmail()]);
$user = User::factory()->create(['account_id' => $account->id, 'email' => \Illuminate\Support\Str::random(32)."@example.com"]);
$company = Company::factory()->create(['account_id' => $account->id]);
$client = Client::factory()->create([