1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-08 20:22:42 +01:00

Merge branch 'v5-develop' into einvoicing-peppol-form-api

Signed-off-by: David Bomba <turbo124@gmail.com>
This commit is contained in:
David Bomba 2024-10-22 07:08:20 +11:00 committed by GitHub
commit 73364b5707
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
150 changed files with 4873 additions and 829 deletions

View File

@ -1 +1 @@
5.10.34
5.10.42

View File

@ -385,9 +385,12 @@ class CreateSingleAccount extends Command
});
$this->countryClients($company, $user);
$cc = ClientContact::where('company_id', $company->id)->latest()->first();
$cc->email = 'user@example.com';
$cc->save();
$this->info("finished");
}
@ -472,13 +475,13 @@ class CreateSingleAccount extends Command
'company_id' => $company->id,
]);
ClientContact::factory()->create([
'user_id' => $user->id,
'client_id' => $client->id,
'company_id' => $company->id,
'is_primary' => 1,
'email' => 'user@example.com',
]);
// ClientContact::factory()->create([
// 'user_id' => $user->id,
// 'client_id' => $client->id,
// 'company_id' => $company->id,
// 'is_primary' => 1,
// 'email' => 'user@example.com',
// ]);
ClientContact::factory()->count(rand(1, 2))->create([
'user_id' => $user->id,
@ -490,7 +493,6 @@ class CreateSingleAccount extends Command
$settings = $client->settings;
$settings->currency_id = "1";
// $settings->use_credits_payment = "always";
$client->settings = $settings;

View File

@ -11,15 +11,18 @@
namespace App\Console\Commands;
use App\Libraries\MultiDB;
use App\Models\Client;
use App\Models\Company;
use App\Utils\Traits\ClientGroupSettingsSaver;
use App\Models\Invoice;
use App\Libraries\MultiDB;
use Illuminate\Console\Command;
use App\Utils\Traits\CleanLineItems;
use App\Utils\Traits\ClientGroupSettingsSaver;
class TypeCheck extends Command
{
use ClientGroupSettingsSaver;
use CleanLineItems;
/**
* The name and signature of the console command.
@ -126,6 +129,13 @@ class TypeCheck extends Command
$this->logMessage("Checking company {$company->id}");
$company->saveSettings($company->settings, $company);
});
Invoice::query()->cursor()->each(function ($invoice){
$this->logMessage("Checking invoice {$invoice->id}");
$invoice->line_items = $this->cleanItems($invoice->line_items);
$invoice->saveQuietly();
});
}
private function logMessage($str)

View File

@ -35,7 +35,7 @@ class TaxModel
$this->regions = $this->init();
} else {
if(is_null($model->seller_subregion)) {
if(is_null($model->seller_subregion)) {//@phpstan-ignore-line
$this->seller_subregion = '';
}

View File

@ -11,17 +11,21 @@
namespace App\Events\Credit;
use App\Models\BaseModel;
use App\Models\Company;
use App\Models\Credit;
use App\Utils\Traits\Invoice\Broadcasting\DefaultResourceBroadcast;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class CreditWasCreated
class CreditWasCreated implements ShouldBroadcast
{
use Dispatchable;
use InteractsWithSockets;
use SerializesModels;
use DefaultResourceBroadcast;
public $credit;
@ -41,5 +45,12 @@ class CreditWasCreated
$this->credit = $credit;
$this->company = $company;
$this->event_vars = $event_vars;
$this->dontBroadcastToCurrentUser();
}
public function broadcastModel(): BaseModel
{
return $this->credit;
}
}

View File

@ -11,17 +11,21 @@
namespace App\Events\Credit;
use App\Models\BaseModel;
use App\Models\Company;
use App\Models\Credit;
use App\Utils\Traits\Invoice\Broadcasting\DefaultResourceBroadcast;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class CreditWasUpdated
class CreditWasUpdated implements ShouldBroadcast
{
use Dispatchable;
use InteractsWithSockets;
use SerializesModels;
use DefaultResourceBroadcast;
public $credit;
@ -41,5 +45,12 @@ class CreditWasUpdated
$this->credit = $credit;
$this->company = $company;
$this->event_vars = $event_vars;
$this->dontBroadcastToCurrentUser();
}
public function broadcastModel(): BaseModel
{
return $this->credit;
}
}

View File

@ -12,10 +12,11 @@
namespace App\Events\Invoice;
use App\Models\BaseModel;
use App\Models\Company;
use App\Models\Invoice;
use App\Models\Payment;
use App\Utils\Traits\Invoice\Broadcasting\DefaultInvoiceBroadcast;
use App\Utils\Traits\Invoice\Broadcasting\DefaultResourceBroadcast;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;
@ -25,7 +26,7 @@ use Illuminate\Queue\SerializesModels;
*/
class InvoiceWasPaid implements ShouldBroadcast
{
use SerializesModels, DefaultInvoiceBroadcast, InteractsWithSockets;
use SerializesModels, DefaultResourceBroadcast, InteractsWithSockets;
/**
* Create a new event instance.
@ -39,4 +40,9 @@ class InvoiceWasPaid implements ShouldBroadcast
{
$this->dontBroadcastToCurrentUser();
}
public function broadcastModel(): BaseModel
{
return $this->invoice;
}
}

View File

@ -11,17 +11,23 @@
namespace App\Events\Invoice;
use App\Models\BaseModel;
use App\Models\Company;
use App\Models\InvoiceInvitation;
use App\Utils\Traits\Invoice\Broadcasting\DefaultResourceBroadcast;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;
use League\Fractal\Manager;
/**
* Class InvoiceWasViewed.
*/
class InvoiceWasViewed
class InvoiceWasViewed implements ShouldBroadcast
{
use SerializesModels;
use InteractsWithSockets;
use DefaultResourceBroadcast;
/**
* Create a new event instance.
@ -32,5 +38,18 @@ class InvoiceWasViewed
*/
public function __construct(public InvoiceInvitation $invitation, public Company $company, public array $event_vars)
{
//
}
public function broadcastModel(): BaseModel
{
return $this->invitation->invoice;
}
public function broadcastManager(Manager $manager): Manager
{
$manager->parseIncludes('client');
return $manager;
}
}

View File

@ -11,16 +11,20 @@
namespace App\Events\Payment;
use App\Models\BaseModel;
use App\Models\Company;
use App\Models\Payment;
use App\Utils\Traits\Invoice\Broadcasting\DefaultResourceBroadcast;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;
/**
* Class PaymentWasUpdated.
*/
class PaymentWasUpdated
class PaymentWasUpdated implements ShouldBroadcast
{
use SerializesModels;
use SerializesModels, InteractsWithSockets, DefaultResourceBroadcast;
/**
* @var Payment
@ -43,5 +47,12 @@ class PaymentWasUpdated
$this->payment = $payment;
$this->company = $company;
$this->event_vars = $event_vars;
$this->dontBroadcastToCurrentUser();
}
public function broadcastModel(): BaseModel
{
return $this->payment;
}
}

View File

@ -62,10 +62,9 @@ class TaskExport extends BaseExport
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->task_report_keys);
$this->input['report_keys'] = array_merge($this->input['report_keys'], array_diff($this->forced_client_fields, $this->input['report_keys']));
}
$this->input['report_keys'] = array_merge($this->input['report_keys'], array_diff($this->forced_client_fields, $this->input['report_keys']));
$query = Task::query()
->withTrashed()
->where('company_id', $this->company->id);
@ -207,15 +206,23 @@ class TaskExport extends BaseExport
$entity['task.end_time'] = ctrans('texts.is_running');
}
$seconds = $task->calcDuration();
$time_log_entry = (isset($item[1]) && $item[1] != 0) ? $item[1] - $item[0] : ctrans('texts.is_running');
if (in_array('task.duration', $this->input['report_keys']) || in_array('duration', $this->input['report_keys'])) {
$seconds = $task->calcDuration();
$entity['task.duration'] = $seconds;
$entity['task.duration_words'] = $seconds > 86400 ? CarbonInterval::seconds($seconds)->locale($this->company->locale())->cascade()->forHumans() : now()->startOfDay()->addSeconds($seconds)->format('H:i:s');
}
$time_log_entry = (isset($item[1]) && $item[1] != 0) ? $item[1] - $item[0] : ctrans('texts.is_running');
if (in_array('task.time_log', $this->input['report_keys']) || in_array('time_log', $this->input['report_keys'])) {
$entity['task.time_log'] = $time_log_entry;
$entity['task.time_log_duration_words'] = is_int($time_log_entry) && $time_log_entry > 86400 ? CarbonInterval::seconds($time_log_entry)->locale($this->company->locale())->cascade()->forHumans() : $time_log_entry;
}
if (in_array('task.time_log_duration_words', $this->input['report_keys']) || in_array('time_log_duration_words', $this->input['report_keys'])) {
$entity['task.time_log_duration_words'] = is_int($time_log_entry) ? CarbonInterval::seconds($time_log_entry)->locale($this->company->locale())->cascade()->forHumans() : $time_log_entry;
}
if (in_array('task.duration_words', $this->input['report_keys']) || in_array('duration_words', $this->input['report_keys'])) {
$entity['task.duration_words'] = $seconds > 86400 ? CarbonInterval::seconds($seconds)->locale($this->company->locale())->cascade()->forHumans() : now()->startOfDay()->addSeconds($seconds)->format('H:i:s');
}
if (in_array('task.billable', $this->input['report_keys']) || in_array('billable', $this->input['report_keys'])) {
@ -227,7 +234,6 @@ class TaskExport extends BaseExport
}
$this->storage_array[] = $entity;
$entity['task.start_date'] = '';
@ -237,7 +243,7 @@ class TaskExport extends BaseExport
$entity['task.duration'] = '';
$entity['task.duration_words'] = '';
$entity['task.time_log'] = '';
$entity['task.time_log_duration_words'];
$entity['task.time_log_duration_words'] = '';
$entity['task.billable'] = '';
$entity['task.item_notes'] = '';

View File

@ -177,6 +177,15 @@ class ExpenseFilters extends QueryFilters
return $this->builder->whereIn('category_id', $categories_keys);
}
public function amount(string $amount = ''): Builder
{
if (strlen($amount) == 0) {
return $this->builder;
}
return $this->builder->where('amount', $amount);
}
public function number(string $number = ''): Builder
{
if (strlen($number) == 0) {

View File

@ -276,7 +276,7 @@ class InvoiceFilters extends QueryFilters
{
$sort_col = explode('|', $sort);
if (!is_array($sort_col) || count($sort_col) != 2 || in_array($sort_col[0], ['documents'])) {
if (!is_array($sort_col) || count($sort_col) != 2 || !in_array($sort_col[0], \Illuminate\Support\Facades\Schema::getColumnListing($this->builder->getModel()->getTable()))) {
return $this->builder;
}
@ -290,9 +290,6 @@ class InvoiceFilters extends QueryFilters
}
if($sort_col[0] == 'number') {
// return $this->builder->orderByRaw('CAST(number AS UNSIGNED), number ' . $dir);
// return $this->builder->orderByRaw("number REGEXP '^[A-Za-z]+$',CAST(number as SIGNED INTEGER),CAST(REPLACE(number,'-','')AS SIGNED INTEGER) ,number");
// return $this->builder->orderByRaw('ABS(number) ' . $dir);
return $this->builder->orderByRaw("REGEXP_REPLACE(invoices.number,'[^0-9]+','')+0 " . $dir);
}

View File

@ -59,7 +59,7 @@ class InvoiceController extends Controller
public function show(ShowInvoiceRequest $request, Invoice $invoice, ?string $hash = null)
{
set_time_limit(0);
$invitation = $invoice->invitations()->where('client_contact_id', auth()->guard('contact')->user()->id)->first();
// @phpstan-ignore-next-line
@ -91,7 +91,6 @@ class InvoiceController extends Controller
return auth()->guard('contact')->user()->client->getSetting('payment_flow') == 'default' ? $this->render('invoices.show', $data) : $this->render('invoices.show_smooth', $data);
// return $this->render('invoices.show_smooth', $data);
}
public function showBlob($hash)

View File

@ -644,8 +644,6 @@ class CreditController extends BaseController
EmailEntity::dispatch($invitation, $credit->company, 'credit');
});
// $credit->sendEvent(Webhook::EVENT_SENT_CREDIT, "client");
if (! $bulk) {
return response()->json(['message' => 'email sent'], 200);
}

View File

@ -647,7 +647,7 @@ class ExpenseController extends BaseController
$user = auth()->user();
foreach ($request->file("documents") as $file) {
ImportEDocument::dispatch($file->get(), $file->getClientOriginalName(), $request->file("documents")->getMimeType(), $user->company());
ImportEDocument::dispatch($file->get(), $file->getClientOriginalName(), $file->getMimeType(), $user->company());
}
return response()->json(['message' => 'Processing....'], 200);

View File

@ -285,7 +285,10 @@ class ImportController extends Controller
'WINDOWS-1251', // CP1251
'UTF-16',
'UTF-32',
'ASCII'
'ASCII',
'WINDOWS-1254', // Turkish, which sometimes includes Georgian
'WINDOWS-1256', // Arabic, which sometimes includes Georgian
'ISO-8859-10',
];
foreach ($data as $key => $value) {

View File

@ -165,9 +165,17 @@ class InvoiceController extends BaseController
{
/** @var \App\Models\User $user */
$user = auth()->user();
/** @var \App\Models\Company $company */
$company = auth()->user()->company();
$invoice = InvoiceFactory::create($user->company()->id, $user->id);
$invoice->date = now()->addSeconds($user->company()->utc_offset())->format('Y-m-d');
$invoice->custom_surcharge_tax1 = $company->custom_surcharge_taxes1;
$invoice->custom_surcharge_tax2 = $company->custom_surcharge_taxes2;
$invoice->custom_surcharge_tax3 = $company->custom_surcharge_taxes3;
$invoice->custom_surcharge_tax4 = $company->custom_surcharge_taxes4;
return $this->itemResponse($invoice);
}

View File

@ -83,9 +83,14 @@ class LicenseController extends BaseController
{
$this->checkLicense();
nlog("License Check::");
nlog(config('ninja.environment'));
nlog(request()->has('license_key'));
nlog(request()->input('license_key','No License Found'));
/* Catch claim license requests */
if (config('ninja.environment') == 'selfhost' && request()->has('license_key')) {
$license_key = request()->input('license_key');
$license_key = trim(request()->input('license_key'));
$product_id = 3;
if(substr($license_key, 0, 3) == 'v5_') {
@ -144,6 +149,8 @@ class LicenseController extends BaseController
}
}
nlog("No license key or environment not set to selfhost");
$error = [
'message' => ctrans('texts.invoice_license_or_environment', ['environment' => config('ninja.environment')]),
'errors' => new stdClass(),
@ -156,15 +163,22 @@ class LicenseController extends BaseController
{
$this->checkLicense();
/* Catch claim license requests */
/* Catch claim license requests */
if (config('ninja.environment') == 'selfhost') {
nlog("Claiming v5 license");
$response = Http::get("https://invoicing.co/claim_license", [
'license_key' => $license_key,
'product_id' => 3,
]);
$payload = $response->json();
nlog("Ninja Server Response");
nlog($payload);
if ($response->successful()) {
$payload = $response->json();
$account = auth()->user()->account;
@ -189,6 +203,8 @@ class LicenseController extends BaseController
}
}
nlog("Environment not set to selfhost - failing");
$error = [
'message' => ctrans('texts.invoice_license_or_environment', ['environment' => config('ninja.environment')]),
'errors' => new stdClass(),

View File

@ -71,6 +71,12 @@ class PreviewController extends BaseController
$entity_obj->footer = empty($entity_obj->footer) ? $settings->{$entity_prop."_footer"} : $entity_obj->footer;
$entity_obj->terms = empty($entity_obj->terms) ? $settings->{$entity_prop."_terms"} : $entity_obj->terms;
$entity_obj->public_notes = empty($entity_obj->public_notes) ? $request->getClient()->public_notes : $entity_obj->public_notes;
$entity_obj->custom_surcharge_tax1 = $client->company->custom_surcharge_taxes1;
$entity_obj->custom_surcharge_tax2 = $client->company->custom_surcharge_taxes2;
$entity_obj->custom_surcharge_tax3 = $client->company->custom_surcharge_taxes3;
$entity_obj->custom_surcharge_tax4 = $client->company->custom_surcharge_taxes4;
$invitation->setRelation($request->entity, $entity_obj);
}

View File

@ -30,7 +30,7 @@ class SmtpController extends BaseController
$company = $user->company();
$smtp_host = $request->input('smtp_host', $company->smtp_host);
$smtp_port = $request->input('smtp_port', $company->smtp_port);
$smtp_port = (int)$request->input('smtp_port', $company->smtp_port);
$smtp_username = $request->input('smtp_username', $company->smtp_username);
$smtp_password = $request->input('smtp_password', $company->smtp_password);
$smtp_encryption = $request->input('smtp_encryption', $company->smtp_encryption ?? 'tls');

View File

@ -25,13 +25,20 @@ class SubdomainController extends BaseController
*/
public function index()
{
$user = auth()->user();
$company = $user->company();
if($company->subdomain == trim(request()->input('subdomain'))){
return response()->json(['message' => 'Current subdomain name.'], 200);
}
if (!MultiDB::checkDomainAvailable(request()->input('subdomain'))) {
return response()->json(['message' => ctrans('texts.subdomain_is_not_available')], 401);
}
if (!preg_match('/^[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?$/', request()->input('subdomain'))) {
return response()->json(['message' => ctrans('texts.subdomain_is_not_available')], 401);
return response()->json(['message' => "Invalid subdomain format."], 401);
}

View File

@ -35,7 +35,6 @@ class CheckClientExistence
auth()->guard('contact')->user()->loadMissing(['company']); // @phpstan-ignore method.notFound
$multiple_contacts = ClientContact::query()
// ->with('client.gateway_tokens', 'company')
->where('email', auth()->guard('contact')->user()->email)
->whereNotNull('email')
->where('email', '<>', '')
@ -66,7 +65,6 @@ class CheckClientExistence
}
session()->put('multiple_contacts', $multiple_contacts);
session()->put('is_silent', request()->has('silent'));
return $next($request);

View File

@ -37,9 +37,9 @@ class CreateAccountRequest extends Request
public function rules()
{
if (Ninja::isHosted()) {
$email_rules = ['bail', 'required', 'email:rfc,dns', new NewUniqueUserRule(), new BlackListRule(), new EmailBlackListRule()];
$email_rules = ['bail', 'required', 'max:255', 'email:rfc,dns', new NewUniqueUserRule(), new BlackListRule(), new EmailBlackListRule()];
} else {
$email_rules = ['bail', 'required', 'email:rfc,dns', new NewUniqueUserRule()];
$email_rules = ['bail', 'required', 'max:255', 'email:rfc,dns', new NewUniqueUserRule()];
}
return [

View File

@ -74,7 +74,7 @@ class UpdateCompanyRequest extends Request
}
if (Ninja::isHosted()) {
$rules['subdomain'] = ['nullable', 'regex:/^[a-zA-Z0-9.-]+[a-zA-Z0-9]$/', new ValidSubdomain()];
$rules['subdomain'] = ['nullable', 'regex:/^[a-zA-Z0-9-]+[a-zA-Z0-9]$/', new ValidSubdomain()];
}
$rules['expense_mailbox'] = ['sometimes','email', 'nullable', new ValidExpenseMailbox(), Rule::unique('companies')->ignore($this->company->id)];

View File

@ -22,7 +22,7 @@ class SendEmailRequest extends Request
{
use MakesHash;
private string $entity_plural = '';
private string $entity_plural = 'invoices';
private string $error_message = '';
public array $templates = [
@ -92,9 +92,8 @@ class SendEmailRequest extends Request
$input['entity_id'] = $this->decodePrimaryKey($input['entity_id']);
}
$this->entity_plural = Str::plural($input['entity']) ?? '';
if (isset($input['entity']) && in_array($input['entity'], ['invoice','quote','credit','recurring_invoice','purchase_order','payment'])) {
$this->entity_plural = Str::plural($input['entity']) ?? '';
$input['entity'] = "App\Models\\".ucfirst(Str::camel($input['entity']));
}
@ -106,13 +105,15 @@ class SendEmailRequest extends Request
})->slice(0, 4)->toArray();
}
$this->replace($input);
}
public function message()
public function messages()
{
return [
'template' => 'Invalid template.',
'template.in' => 'Template :input is anot a valid template.',
'entity.in' => 'Entity :input is not a valid entity.'
];
}
@ -134,8 +135,7 @@ class SendEmailRequest extends Request
}
/*Make sure we have all the require ingredients to send a template*/
if (isset($input['entity']) && array_key_exists('entity_id', $input) && is_string($input['entity']) && $input['entity_id']) {
if (isset($input['entity']) && array_key_exists('entity_id', $input) && in_array($input['entity'], ['App\Models\Invoice','App\Models\Quote','App\Models\Credit','App\Models\RecurringInvoice','App\Models\PurchaseOrder','App\Models\Payment'])) {
$company = $user->company();
@ -148,11 +148,12 @@ class SendEmailRequest extends Request
if ($entity_obj && ($company->id == $entity_obj->company_id) && $user->can('edit', $entity_obj)) {
return true;
}
} else {
$this->error_message = "Invalid entity or entity_id";
}
else {
$this->error_message = "Invalid entity or entity_id";
}
}
return false;
return true;
}
protected function failedAuthorization()

View File

@ -331,6 +331,7 @@ class CompanyImport implements ShouldQueue
->purgeCompanyData()
->importCompany()
->importData()
->miscTransformations()
->postImportCleanup();
$data = [
@ -359,6 +360,52 @@ class CompanyImport implements ShouldQueue
}
private function miscTransformations()
{
Invoice::withTrashed()
->where('company_id', $this->company->id)
->cursor()
->each(function ($invoice){
$items = $invoice->line_items;
foreach($items as $key => $value)
{
if(isset($value->task_id) && strlen($value->task_id) >1) {
$t_id = $this->transformId('tasks',$value->task_id);
if($t = Task::withTrashed()->where('company_id', $this->company->id)->where('id',$t_id)->first())
{
$items[$key]->task_id = $t->hashed_id;
}
}
if (isset($value->expense_id) && strlen($value->expense_id) > 1) {
$e_id = $this->transformId('expenses', $value->expense_id);
if ($e = Expense::withTrashed()->where('company_id', $this->company->id)->where('id', $e_id)->first()) {
$items[$key]->expense_id = $e->hashed_id;
}
}
}
$invoice->line_items = array_values($items);
$invoice->saveQuietly();
});
return $this;
}
//
private function postImportCleanup()
{

View File

@ -11,16 +11,17 @@
namespace App\Jobs\EDocument;
use App\Models\Expense;
use App\Services\EDocument\Imports\ParseEDocument;
use App\Utils\TempFile;
use Exception;
use App\Models\Company;
use App\Models\Expense;
use App\Utils\TempFile;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use App\Services\EDocument\Imports\ParseEDocument;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use App\Services\EDocument\Imports\ZugferdEDocument;
@ -31,8 +32,6 @@ class ImportEDocument implements ShouldQueue
use Queueable;
use SerializesModels;
public $tries = 1;
public function __construct(private readonly string $file_content, private string $file_name, private string $file_mime_type, private Company $company)
{
@ -46,7 +45,6 @@ class ImportEDocument implements ShouldQueue
*/
public function handle(): Expense
{
$file = TempFile::UploadedFileFromRaw($this->file_content, $this->file_name, $this->file_mime_type);
return (new ParseEDocument($file, $this->company))->run();
@ -55,7 +53,7 @@ class ImportEDocument implements ShouldQueue
public function middleware()
{
return [new WithoutOverlapping($this->company->company_key)];
return [new WithoutOverlapping($this->company->company_key."_expense_import_".$this->file_name)];
}
public function failed($exception = null)
@ -63,7 +61,8 @@ class ImportEDocument implements ShouldQueue
if ($exception) {
nlog("EXCEPTION:: ImportEDocument:: " . $exception->getMessage());
}
$this->fail($exception); //manually fail - prevents future jobs with the same name from being discarded
config(['queue.failed.driver' => null]);
}
}

View File

@ -417,7 +417,7 @@ class NinjaMailerJob implements ShouldQueue
$company = $this->company;
$smtp_host = $company->smtp_host ?? '';
$smtp_port = $company->smtp_port;
$smtp_port = (int)$company->smtp_port;
$smtp_username = $company->smtp_username ?? '';
$smtp_password = $company->smtp_password ?? '';
$smtp_encryption = $company->smtp_encryption ?? 'tls';

View File

@ -59,7 +59,7 @@ class CleanStaleInvoiceOrder implements ShouldQueue
Invoice::query()
->withTrashed()
->where('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('status_id', Invoice::STATUS_SENT)
->where('updated_at', '<', now()->subHour())
->where('balance', '>', 0)
->whereJsonContains('line_items', ['type_id' => '3'])
@ -68,6 +68,85 @@ class CleanStaleInvoiceOrder implements ShouldQueue
$invoice->service()->removeUnpaidGatewayFees();
});
Invoice::query()
->withTrashed()
->where('status_id', Invoice::STATUS_PARTIAL)
->where('balance', '>', 0)
->whereJsonContains('line_items', ['type_id' => '3'])
->cursor()
->each(function ($invoice) {
$type_3_count = 0;
$type_4_count = 0;
foreach ($invoice->line_items as $line_item) {
if ($line_item->type_id == '3') {
$type_3_count++;
} elseif ($line_item->type_id == '4') {
$type_4_count++;
}
}
if ($type_4_count == 1) {
$invoice->service()->removeUnpaidGatewayFees();
} elseif ($type_3_count == 1) {
$items = $invoice->line_items;
foreach ($items as $key => $value) {
if ($value->type_id == "3") {
$items[$key]->type_id = "4";
}
}
$invoice->line_items = array_values($items);
$invoice->calc()->getInvoice();
}
});
Invoice::query()
->withTrashed()
->where('status_id', Invoice::STATUS_PAID)
->whereJsonContains('line_items', ['type_id' => '3'])
->cursor()
->each(function ($invoice) {
$type_3_count = 0;
$type_4_count = 0;
foreach ($invoice->line_items as $line_item) {
if ($line_item->type_id == '3') {
$type_3_count++;
} elseif ($line_item->type_id == '4') {
$type_4_count++;
}
}
if ($type_4_count == 0 && $type_3_count == 1) {
$items = $invoice->line_items;
foreach ($items as $key => $value) {
if ($value->type_id == "3") {
$items[$key]->type_id = "4";
}
}
$invoice->line_items = array_values($items);
$invoice->saveQuietly();
}
});
return;
}
@ -76,14 +155,15 @@ class CleanStaleInvoiceOrder implements ShouldQueue
MultiDB::setDB($db);
Invoice::query()
->withTrashed()
->where('is_proforma', 1)
->where('created_at', '<', now()->subHour())
->cursor()
->each(function ($invoice) use ($repo) {
$invoice->is_proforma = false;
$repo->delete($invoice);
});
->withTrashed()
->where('status_id', Invoice::STATUS_SENT)
->where('is_proforma', 1)
->where('created_at', '<', now()->subHour())
->cursor()
->each(function ($invoice) use ($repo) {
$invoice->is_proforma = false;
$repo->delete($invoice);
});
Invoice::query()
->withTrashed()
@ -96,6 +176,85 @@ class CleanStaleInvoiceOrder implements ShouldQueue
$invoice->service()->removeUnpaidGatewayFees();
});
Invoice::query()
->withTrashed()
->where('status_id', Invoice::STATUS_PARTIAL)
->whereJsonContains('line_items', ['type_id' => '3'])
->cursor()
->each(function ($invoice) {
$type_3_count = 0;
$type_4_count = 0;
foreach ($invoice->line_items as $line_item) {
if ($line_item->type_id == '3') {
$type_3_count++;
} elseif ($line_item->type_id == '4') {
$type_4_count++;
}
}
if ($type_4_count == 1) {
$invoice->service()->removeUnpaidGatewayFees();
} elseif ($type_3_count == 1) {
$items = $invoice->line_items;
foreach ($items as $key => $value) {
if ($value->type_id == "3") {
$items[$key]->type_id = "4";
}
}
$invoice->line_items = array_values($items);
$invoice->calc()->getInvoice();
}
});
Invoice::query()
->withTrashed()
->where('status_id', Invoice::STATUS_PAID)
->whereJsonContains('line_items', ['type_id' => '3'])
->cursor()
->each(function ($invoice) {
$type_3_count = 0;
$type_4_count = 0;
foreach ($invoice->line_items as $line_item) {
if ($line_item->type_id == '3') {
$type_3_count++;
} elseif ($line_item->type_id == '4') {
$type_4_count++;
}
}
if ($type_4_count == 0 && $type_3_count == 1) {
$items = $invoice->line_items;
foreach ($items as $key => $value) {
if ($value->type_id == "3") {
$items[$key]->type_id = "4";
}
}
$invoice->line_items = array_values($items);
$invoice->saveQuietly();
}
});
\DB::connection($db)->table('password_resets')->where('created_at', '<', now()->subHours(12))->delete();
}

View File

@ -432,15 +432,20 @@ class Import implements ShouldQueue
}
}
$rules = (new UpdateCompanyRequest())->rules();
// $rules = (new UpdateCompanyRequest())->rules();
unset($rules['expense_mailbox']);
$validator = Validator::make($data, $rules);
// unset($rules['expense_mailbox']);
// unset($rules['e_invoice']);
// unset($rules['smtp_port']);
// unset($rules['settings']);
if ($validator->fails()) {
throw new MigrationValidatorFailed(json_encode($validator->errors()));
}
// $validator = Validator::make($data, $rules);
// nlog($validator->errors());
// if ($validator->fails()) {
// throw new MigrationValidatorFailed(json_encode($validator->errors()));
// }
if (isset($data['account_id'])) {
unset($data['account_id']);
@ -1566,6 +1571,7 @@ class Import implements ShouldQueue
} catch(\Exception $e) {
nlog($e->getMessage());
//do nothing, gracefully :)
}
}

View File

@ -101,7 +101,7 @@ class Register extends Component
$this->register_fields = [...collect($this->subscription->company->client_registration_fields ?? [])->toArray()];
$first_gateway = collect($this->subscription->company->company_gateways)
$first_gateway = collect($this->subscription->company->company_gateways()->withoutTrashed()->get())
->sortBy('sort_order')
->first();

View File

@ -202,11 +202,7 @@ class RegisterOrLogin extends Component
$this->register_fields = [...collect($this->subscription->company->client_registration_fields ?? [])->toArray()];
// if ($this->subscription->company->settings->client_portal_terms || $this->subscription->company->settings->client_portal_privacy_policy) {
// $this->register_fields[] = ['key' => 'terms', 'required' => true, 'visible' => 'true'];
// }
$first_gateway = collect($this->subscription->company->company_gateways)
$first_gateway = collect($this->subscription->company->company_gateways()->withoutTrashed()->get())
->sortBy('sort_order')
->first();

View File

@ -116,7 +116,7 @@ class Summary extends Component
return \sprintf(
'%s/%s',
Number::formatMoney($recurring + $recurring_optional, $this->subscription->company),
RecurringInvoice::frequencyForKey($this->subscription->frequency_id)
RecurringInvoice::frequencyForKey($this->subscription->frequency_id ?? 0)
);
}

View File

@ -105,15 +105,12 @@ class InvoicePay extends Component
#[On('terms-accepted')]
public function termsAccepted()
{
nlog("Terms accepted");
// $this->invite = \App\Models\InvoiceInvitation::withTrashed()->find($this->invitation_id)->withoutRelations();
$this->terms_accepted = true;
}
#[On('signature-captured')]
public function signatureCaptured($base64)
{
nlog("signature captured");
$this->signature_accepted = true;
$invite = \App\Models\InvoiceInvitation::withTrashed()->find($this->invitation_id);
@ -141,7 +138,6 @@ class InvoicePay extends Component
$this->setContext('amount', $amount);
$this->setContext('pre_payment', false);
$this->setContext('is_recurring', false);
$this->setContext('invitation_id', $this->invitation_id);
$this->payment_method_accepted = true;
@ -178,9 +174,7 @@ class InvoicePay extends Component
empty($contact->client->{$_field})
|| is_null($contact->client->{$_field}) //@phpstan-ignore-line
) {
return $this->required_fields = true;
}
}
@ -190,7 +184,7 @@ class InvoicePay extends Component
}
}
}
return $this->required_fields = false;
}
@ -231,21 +225,27 @@ class InvoicePay extends Component
public function mount()
{
$this->resetContext();
MultiDB::setDb($this->db);
// @phpstan-ignore-next-line
$invite = \App\Models\InvoiceInvitation::with('contact.client', 'company')->withTrashed()->find($this->invitation_id);
$client = $invite->contact->client;
$settings = $client->getMergedSettings();
$this->setContext('contact', $invite->contact); // $this->context['contact'] = $invite->contact;
$this->setContext('settings', $settings); // $this->context['settings'] = $settings;
$this->setContext('db', $this->db); // $this->context['db'] = $this->db;
$this->setContext('invitation_id', $this->invitation_id);
if(is_array($this->invoices))
$this->invoices = Invoice::find($this->transformKeys($this->invoices));
elseif ($this->invoices instanceof \Illuminate\Support\Collection) {
$this->invoices = Invoice::find($this->invoices->pluck('id'));
}
$invoices = $this->invoices->filter(function ($i) {
$i = $i->service()
->markSent()
@ -292,9 +292,9 @@ class InvoicePay extends Component
public function exception($e, $stopPropagation)
{
app('sentry')->captureException($e);
nlog($e->getMessage());
$stopPropagation();
}

View File

@ -78,9 +78,7 @@ class PaymentMethod extends Component
public function exception($e, $stopPropagation)
{
nlog($e->getMessage());
app('sentry')->captureException($e);
$stopPropagation();
}
}

View File

@ -58,16 +58,22 @@ class ProcessPayment extends Component
throw new PaymentFailed($responder_data['error'], 400);
}
$driver = $company_gateway
->driver($invitation->contact->client) // @phpstan-ignore-line
->setPaymentMethod($data['payment_method_id'])
->setPaymentHash($responder_data['payload']['ph']);
if(isset($responder_data['component']) && $responder_data['component'] == 'CreditPaymentComponent'){
$this->payment_view = $responder_data['view'];
$this->payment_data_payload = $responder_data['payload'];
}
else {
$driver = $company_gateway
->driver($invitation->contact->client) // @phpstan-ignore-line
->setPaymentMethod($data['payment_method_id'])
->setPaymentHash($responder_data['payload']['ph']);
$this->payment_data_payload = $driver->processPaymentViewData($responder_data['payload']);
$this->payment_view = $driver->livewirePaymentView(
$this->payment_data_payload,
);
$this->payment_data_payload = $driver->processPaymentViewData($responder_data['payload']);
$this->payment_view = $driver->livewirePaymentView(
$this->payment_data_payload,
);
}
$this->isLoading = false;
@ -87,6 +93,8 @@ class ProcessPayment extends Component
public function exception($e, $stopPropagation)
{
app('sentry')->captureException($e);
$errors = session()->get('errors', new \Illuminate\Support\ViewErrorBag());
$bag = new \Illuminate\Support\MessageBag();

View File

@ -61,12 +61,12 @@ class RequiredFields extends Component
$this->fields = $this->getContext()['fields'];
$contact = auth()->guard('contact')->user();
$this->company_gateway = CompanyGateway::withTrashed()
->with('company')
->find($this->getContext()['company_gateway_id']);
$contact = auth()->guard('contact')->user();
$this->client_name = $contact->client->name;
$this->contact_first_name = $contact->first_name;
$this->contact_last_name = $contact->last_name;
@ -138,9 +138,8 @@ class RequiredFields extends Component
public function exception($e, $stopPropagation)
{
app('sentry')->captureException($e);
nlog($e->getMessage());
$stopPropagation();
}

View File

@ -58,7 +58,7 @@ use Laracasts\Presenter\PresentableTrait;
* @property bool $is_scheduler_running
* @property int|null $trial_duration
* @property bool $is_onboarding
* @property object|null $onboarding
* @property object|array|null $onboarding
* @property bool $is_migrated
* @property string|null $platform
* @property int|null $hosted_client_count

View File

@ -41,10 +41,10 @@ use Laracasts\Presenter\PresentableTrait;
* @property bool $update_products
* @property bool $show_product_details
* @property bool $client_can_register
* @property int $custom_surcharge_taxes1
* @property int $custom_surcharge_taxes2
* @property int $custom_surcharge_taxes3
* @property int $custom_surcharge_taxes4
* @property bool $custom_surcharge_taxes1
* @property bool $custom_surcharge_taxes2
* @property bool $custom_surcharge_taxes3
* @property bool $custom_surcharge_taxes4
* @property int $show_product_cost
* @property int $enabled_tax_rates
* @property int $enabled_modules
@ -577,8 +577,8 @@ class Company extends BaseModel
}
public function activities(): HasMany
{
return $this->hasMany(Activity::class)->where('account_id', $this->account_id)->orderBy('id', 'DESC')->take(50);
{
return $this->hasMany(Activity::class)->orderBy('id', 'DESC')->take(50);
}
/**

View File

@ -146,12 +146,9 @@ class Expense extends BaseModel
];
public static array $bulk_update_columns = [
'tax_rate1',
'tax_name1',
'tax_rate2',
'tax_name2',
'tax_rate3',
'tax_name3',
'tax1',
'tax2',
'tax3',
'custom_value1',
'custom_value2',
'custom_value3',

View File

@ -467,7 +467,7 @@ class Invoice extends BaseModel
public function isPayable(): bool
{
if($this->is_deleted || $this->status_id == self::STATUS_PAID) {
if($this->is_deleted || $this->status_id == self::STATUS_PAID || $this->balance < 0) {
return false;
} elseif ($this->status_id == self::STATUS_DRAFT && $this->is_deleted == false) {
return true;

View File

@ -216,7 +216,7 @@ class Product extends BaseModel
return $converter->convert($this->notes ?? '');
}
public static function markdownHelp(string $notes = '')
public static function markdownHelp(?string $notes = '')
{
$converter = new CommonMarkConverter([
@ -226,7 +226,7 @@ class Product extends BaseModel
],
]);
return $converter->convert($notes);
return $converter->convert($notes ?? '');
}

View File

@ -247,12 +247,9 @@ class RecurringInvoice extends BaseModel
protected $touches = [];
public static array $bulk_update_columns = [
'tax_rate1',
'tax_name1',
'tax_rate2',
'tax_name2',
'tax_rate3',
'tax_name3',
'tax1',
'tax2',
'tax3',
'custom_value1',
'custom_value2',
'custom_value3',

View File

@ -201,4 +201,23 @@ class Subscription extends BaseModel
return null;
}
}
/**
* Calculates the maximum product quantity available
*
* @param mixed $product
* @return int
*/
public function maxQuantity(mixed $product): int
{
$max_quantity = data_get($product, 'max_quantity', 0);
$in_stock_quantity = data_get($product, 'in_stock_quantity', 0);
$max_limit = 100;
if(!$this->use_inventory_management)
return $max_quantity != 0 ? $max_quantity : $max_limit;
return $max_quantity !=0 ? $max_quantity : min($max_limit, $in_stock_quantity);
}
}

View File

@ -118,6 +118,7 @@ class Vendor extends BaseModel
'language_id',
'classification',
'is_tax_exempt',
'routing_id',
];
protected $casts = [

View File

@ -404,8 +404,7 @@ class BaseDriver extends AbstractPaymentDriver
$item->gross_line_total = round($item->gross_line_total, 2);
return $item;
})
->whereIn('type_id', ['3'])
// ->where('gross_line_total', '<=', round($fee_total,2))
->whereIn('type_id', ['3','4'])
->count();
if($invoice && $fee_count == 0){
@ -456,12 +455,7 @@ class BaseDriver extends AbstractPaymentDriver
$new_balance = $invoice->balance;
if (floatval($new_balance) - floatval($balance) != 0) {
$adjustment = $new_balance - $balance;
$invoice
->ledger()
->updateInvoiceBalance($adjustment, 'Adjustment for adding gateway fee **Base Driver**');
$adjustment = $new_balance - $balance;
$invoice->client->service()->calculateBalance();
}

View File

@ -150,7 +150,7 @@ class ACH implements MethodInterface, LivewireMethodInterface
]);
if ($result->success) {
$this->braintree->logSuccessfulGatewayResponse(['response' => $request->server_response, 'data' => $this->braintree->payment_hash], SystemLog::TYPE_BRAINTREE);
$this->braintree->logSuccessfulGatewayResponse(['response' => $request->server_response, 'data' => $this->braintree->payment_hash->data], SystemLog::TYPE_BRAINTREE);
return $this->processSuccessfulPayment($result);
}

View File

@ -145,7 +145,7 @@ class CreditCard implements LivewireMethodInterface
}
if ($result->success) {
$this->braintree->logSuccessfulGatewayResponse(['response' => $request->server_response, 'data' => $this->braintree->payment_hash], SystemLog::TYPE_BRAINTREE);
$this->braintree->logSuccessfulGatewayResponse(['response' => $request->server_response, 'data' => $this->braintree->payment_hash->data], SystemLog::TYPE_BRAINTREE);
if ($request->store_card && is_null($request->token)) {
$payment_method = $this->braintree->gateway->paymentMethod()->find($token);

View File

@ -83,7 +83,7 @@ class PayPal implements LivewireMethodInterface
if ($result->success) {
$this->braintree->logSuccessfulGatewayResponse(
['response' => $request->server_response, 'data' => $this->braintree->payment_hash],
['response' => $request->server_response, 'data' => $this->braintree->payment_hash->data],
SystemLog::TYPE_BRAINTREE
);

View File

@ -120,7 +120,7 @@ class CreditCard implements LivewireMethodInterface
if ($charge->status == 'complete') {
$this->powerboard->logSuccessfulGatewayResponse(['response' => $charge, 'data' => $this->powerboard->payment_hash], SystemLog::TYPE_POWERBOARD);
$this->powerboard->logSuccessfulGatewayResponse(['response' => $charge, 'data' => $this->powerboard->payment_hash->data], SystemLog::TYPE_POWERBOARD);
$vt = $charge->customer->payment_source->vault_token;
@ -264,7 +264,7 @@ class CreditCard implements LivewireMethodInterface
return 'gateways.powerboard.credit_card.pay_livewire';
}
public function tokenBilling($request, $cgt, $client_present = false)
public function tokenBilling($cgt, $client_present = false)
{
$payload = [
@ -291,7 +291,7 @@ class CreditCard implements LivewireMethodInterface
nlog($charge);
$this->powerboard->logSuccessfulGatewayResponse(['response' => $charge, 'data' => $this->powerboard->payment_hash], SystemLog::TYPE_POWERBOARD);
$this->powerboard->logSuccessfulGatewayResponse(['response' => $charge, 'data' => $this->powerboard->payment_hash->data], SystemLog::TYPE_POWERBOARD);
return $this->processSuccessfulPayment($charge);
}
@ -352,7 +352,7 @@ class CreditCard implements LivewireMethodInterface
->where('token', $request->token)
->first();
return $this->tokenBilling($request, $cgt, true);
return $this->tokenBilling($cgt, true);
}
elseif($request->browser_details)
@ -389,7 +389,7 @@ class CreditCard implements LivewireMethodInterface
nlog($charge);
if ($charge->status == 'complete') {
$this->powerboard->logSuccessfulGatewayResponse(['response' => $charge, 'data' => $this->powerboard->payment_hash], SystemLog::TYPE_POWERBOARD);
$this->powerboard->logSuccessfulGatewayResponse(['response' => $charge, 'data' => $this->powerboard->payment_hash->data], SystemLog::TYPE_POWERBOARD);
$vt = $charge->customer->payment_source->vault_token;

View File

@ -175,6 +175,15 @@ class CBAPowerBoardPaymentDriver extends BaseDriver
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
{
$this->init();
$this->setPaymentMethod($cgt->gateway_type_id);
$this->setPaymentHash($payment_hash);
$this->setClient($cgt->client);
return $this->payment_method->tokenBilling($cgt, false);
}
public function importCustomers()

View File

@ -58,14 +58,9 @@ class CustomPaymentDriver extends BaseDriver
return $this;
}
/**
* View for displaying custom content of the driver.
*
* @param array $data
* @return mixed
*/
public function processPaymentView($data)
public function paymentData(array $data): array
{
$variables = [];
if (count($this->payment_hash->invoices()) > 0) {
@ -87,9 +82,33 @@ class CustomPaymentDriver extends BaseDriver
$data['gateway'] = $this;
$data['payment_hash'] = $this->payment_hash->hash;
return $data;
}
/**
* View for displaying custom content of the driver.
*
* @param array $data
* @return mixed
*/
public function processPaymentView($data)
{
$data = $this->paymentData($data);
return render('gateways.custom.payment', $data);
}
public function livewirePaymentView(array $data): string
{
return 'gateways.custom.pay_livewire';
}
public function processPaymentViewData(array $data): array
{
return $this->paymentData($data);
}
/**
* Processing method for payment. Should never be reached with this driver.
*
@ -117,7 +136,7 @@ class CustomPaymentDriver extends BaseDriver
$gateway_response = json_decode($request->gateway_response);
if ($gateway_response->status == 'COMPLETED') {
$this->logSuccessfulGatewayResponse(['response' => json_decode($request->gateway_response), 'data' => $payment_hash], SystemLog::TYPE_CUSTOM);
$this->logSuccessfulGatewayResponse(['response' => json_decode($request->gateway_response), 'data' => $payment_hash->data], SystemLog::TYPE_CUSTOM);
$data = [
'payment_method' => '',

View File

@ -84,7 +84,7 @@ class CreditCard implements LivewireMethodInterface
if ($payment->status === 'paid') {
$this->mollie->logSuccessfulGatewayResponse(
['response' => $payment, 'data' => $this->mollie->payment_hash],
['response' => $payment, 'data' => $this->mollie->payment_hash->data],
SystemLog::TYPE_MOLLIE
);
@ -149,7 +149,7 @@ class CreditCard implements LivewireMethodInterface
if ($payment->status === 'paid') {
$this->mollie->logSuccessfulGatewayResponse(
['response' => $payment, 'data' => $this->mollie->payment_hash],
['response' => $payment, 'data' => $this->mollie->payment_hash->data],
SystemLog::TYPE_MOLLIE
);

View File

@ -208,7 +208,7 @@ class CreditCard implements LivewireMethodInterface
$this->payfast->payment_hash->save();
if ($response_array['payment_status'] == 'COMPLETE') {
$this->payfast->logSuccessfulGatewayResponse(['response' => $response_array, 'data' => $this->payfast->payment_hash], SystemLog::TYPE_PAYFAST);
$this->payfast->logSuccessfulGatewayResponse(['response' => $response_array, 'data' => $this->payfast->payment_hash->data], SystemLog::TYPE_PAYFAST);
return $this->processSuccessfulPayment($response_array);
} else {

View File

@ -187,7 +187,7 @@ class CreditCard implements LivewireMethodInterface
$response = $this->paytrace->gatewayRequest('/v1/transactions/sale/by_customer', $data);
if ($response->success ?? false) {
$this->paytrace->logSuccessfulGatewayResponse(['response' => $response, 'data' => $this->paytrace->payment_hash], SystemLog::TYPE_PAYTRACE);
$this->paytrace->logSuccessfulGatewayResponse(['response' => $response, 'data' => $this->paytrace->payment_hash->data], SystemLog::TYPE_PAYTRACE);
return $this->processSuccessfulPayment($response);
}

View File

@ -76,7 +76,7 @@ class ApplePay
$response_handler = new CreditCard($this->stripe_driver);
if ($server_response->status == 'succeeded') {
$this->stripe_driver->logSuccessfulGatewayResponse(['response' => json_decode($request->gateway_response), 'data' => $this->stripe_driver->payment_hash], SystemLog::TYPE_STRIPE);
$this->stripe_driver->logSuccessfulGatewayResponse(['response' => json_decode($request->gateway_response), 'data' => $this->stripe_driver->payment_hash->data], SystemLog::TYPE_STRIPE);
return $response_handler->processSuccessfulPayment();
}

View File

@ -106,7 +106,7 @@ class BACS implements LivewireMethodInterface
$this->stripe->payment_hash->save();
if ($state['payment_intent']->status == 'processing') {
$this->stripe->logSuccessfulGatewayResponse(['response' => $state['payment_intent'], 'data' => $this->stripe->payment_hash], SystemLog::TYPE_STRIPE);
$this->stripe->logSuccessfulGatewayResponse(['response' => $state['payment_intent'], 'data' => $this->stripe->payment_hash->data], SystemLog::TYPE_STRIPE);
return $this->processSuccessfulPayment($state['payment_intent']);
}

View File

@ -136,7 +136,7 @@ class BrowserPay implements MethodInterface, LivewireMethodInterface
$gateway_response = $this->stripe->payment_hash->data->gateway_response;
$payment_intent = $this->stripe->payment_hash->data->payment_intent;
$this->stripe->logSuccessfulGatewayResponse(['response' => $gateway_response, 'data' => $this->stripe->payment_hash], SystemLog::TYPE_STRIPE);
$this->stripe->logSuccessfulGatewayResponse(['response' => $gateway_response, 'data' => $this->stripe->payment_hash->data], SystemLog::TYPE_STRIPE);
$payment_method = $this->stripe->getStripePaymentMethod($gateway_response->payment_method);

View File

@ -118,7 +118,7 @@ class CreditCard implements LivewireMethodInterface
$server_response = $this->stripe->payment_hash->data->server_response;
if ($server_response->status == 'succeeded') {
$this->stripe->logSuccessfulGatewayResponse(['response' => json_decode($request->gateway_response), 'data' => $this->stripe->payment_hash], SystemLog::TYPE_STRIPE);
$this->stripe->logSuccessfulGatewayResponse(['response' => json_decode($request->gateway_response), 'data' => $this->stripe->payment_hash->data], SystemLog::TYPE_STRIPE);
return $this->processSuccessfulPayment();
}

View File

@ -396,23 +396,24 @@ class BaseRepository
public function bulkUpdate(\Illuminate\Database\Eloquent\Builder $model, string $column, mixed $new_value): void
{
/** Handle taxes being updated */
if(in_array($column, ['tax_name1','tax_name2','tax_name3'])) {
if(in_array($column, ['tax1','tax2','tax3'])) {
$parts = explode("||", $new_value);
if (count($parts) !== 2)
return;
$tax_name = trim($parts[0]);
$tax_name_column = str_replace("tax", "tax_name", $column);
$rate = filter_var($parts[1], FILTER_VALIDATE_FLOAT);
$tax_name = $parts[0];
if ($rate === false)
return;
$taxrate_column = str_replace("name", "rate", $column);
$taxrate_column = str_replace("tax", "tax_rate", $column);
$model->update([
$column => $tax_name,
$tax_name_column => $tax_name,
$taxrate_column => $rate,
]);
return;

View File

@ -78,7 +78,7 @@ class ClientRepository extends BaseRepository
$client->save();
if (! isset($client->number) || empty($client->number) || strlen($client->number) == 0) {
if (! isset($client->number) || empty($client->number) || strlen($client->number ?? '') == 0) {//@phpstan-ignore-line
$x = 1;
do {

View File

@ -105,7 +105,7 @@ class PaymentMigrationRepository extends BaseRepository
}
/*Ensure payment number generated*/
if (! $payment->number || strlen($payment->number) == 0) {
if (! $payment->number || strlen($payment->number) == 0) {//@phpstan-ignore-line
$payment->number = $payment->client->getNextPaymentNumber($payment->client, $payment);
}

View File

@ -133,7 +133,7 @@ class PaymentRepository extends BaseRepository
}
/*Ensure payment number generated*/
if (! $payment->number || strlen($payment->number) == 0) {
if (! $payment->number || strlen($payment->number ?? '') == 0) { //@phpstan-ignore-line
$payment->service()->applyNumber();
}

View File

@ -72,7 +72,12 @@ class UserRepository extends BaseRepository
$user->confirmation_code = $this->createDbHash($company->db);
}
$user->account_id = $account->id;
//@18-10-2024 - ensure no cross account linkage.
if(is_numeric($user->account_id) && $user->account_id != $account->id){
throw new \Illuminate\Auth\Access\AuthorizationException("Illegal operation encountered for {$user->hashed_id}",401);
}
$user->account_id = $account->id;//@todo we should never change the account_id if it is set at this point.
if (strlen($user->password) >= 1) {
$user->has_password = true;

View File

@ -71,6 +71,9 @@ class Merge extends AbstractService
$this->mergable_client->forceDelete();
$this->client->credit_balance = $this->client->service()->getCreditBalance();
$this->client->saveQuietly();
return $this->client;
}

View File

@ -232,8 +232,7 @@ class LivewireInstantPayment
];
if ($this->is_credit_payment) {
$this->mergeResponder(['success' => true, 'component' => 'CreditPaymentComponent', 'payload' => $data]);
$this->mergeResponder(['success' => true, 'view' => 'gateways.credit.pay_livewire', 'component' => 'CreditPaymentComponent', 'payload' => $data]);
return $this->getResponder();
}

View File

@ -152,9 +152,9 @@ class CreditService
return $this;
}
public function markSent()
public function markSent($fire_event = false)
{
$this->credit = (new MarkSent($this->credit->client, $this->credit))->run();
$this->credit = (new MarkSent($this->credit->client, $this->credit))->run($fire_event);
return $this;
}

View File

@ -43,7 +43,7 @@ class TriggeredActions extends AbstractService
}
if ($this->request->has('mark_sent') && $this->request->input('mark_sent') == 'true') {
$this->credit = $this->credit->service()->markSent()->save();
$this->credit = $this->credit->service()->markSent(true)->save();
}
if ($this->request->has('save_default_footer') && $this->request->input('save_default_footer') == 'true') {

View File

@ -0,0 +1,203 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Services\EDocument\Adapters\CII;
use App\Services\EDocument\Interfaces\PaymentMeansInterface;
class PaymentMeans implements PaymentMeansInterface
{
public array $payment_means_codelist = [
'1' => 'Instrument not defined',
'2' => 'Automated clearing house credit',
'3' => 'Automated clearing house debit',
'4' => 'ACH demand debit reversal',
'5' => 'ACH demand credit reversal',
'6' => 'ACH demand credit',
'7' => 'ACH demand debit',
'8' => 'Hold',
'9' => 'National or regional clearing',
'10' => 'In cash',
'11' => 'ACH savings credit reversal',
'12' => 'ACH savings debit reversal',
'13' => 'ACH savings credit',
'14' => 'ACH savings debit',
'15' => 'Bookentry credit',
'16' => 'Bookentry debit',
'17' => 'ACH demand cash concentration/disbursement (CCD) credit',
'18' => 'ACH demand cash concentration/disbursement (CCD) debit',
'19' => 'ACH demand corporate trade payment (CTP) credit',
'20' => 'Cheque',
'21' => 'Banker\'s draft',
'22' => 'Certified banker\'s draft',
'23' => 'Bank cheque (issued by a banking or similar establishment)',
'24' => 'Bill of exchange awaiting acceptance',
'25' => 'Certified cheque',
'26' => 'Local cheque',
'27' => 'ACH demand corporate trade payment (CTP) debit',
'28' => 'ACH demand corporate trade exchange (CTX) credit',
'29' => 'ACH demand corporate trade exchange (CTX) debit',
'30' => 'Credit transfer',
'31' => 'Debit transfer',
'32' => 'ACH demand cash concentration/disbursement plus (CCD+)',
'33' => 'ACH demand cash concentration/disbursement plus (CCD+)',
'34' => 'ACH prearranged payment and deposit (PPD)',
'35' => 'ACH savings cash concentration/disbursement (CCD) credit',
'36' => 'ACH savings cash concentration/disbursement (CCD) debit',
'37' => 'ACH savings corporate trade payment (CTP) credit',
'38' => 'ACH savings corporate trade payment (CTP) debit',
'39' => 'ACH savings corporate trade exchange (CTX) credit',
'40' => 'ACH savings corporate trade exchange (CTX) debit',
'41' => 'ACH savings cash concentration/disbursement plus (CCD+)',
'42' => 'Payment to bank account',
'43' => 'ACH savings cash concentration/disbursement plus (CCD+)',
'44' => 'Accepted bill of exchange',
'45' => 'Referenced home-banking credit transfer',
'46' => 'Interbank debit transfer',
'47' => 'Home-banking debit transfer',
'48' => 'Bank card',
'49' => 'Direct debit',
'50' => 'Payment by postgiro',
'51' => 'FR, norme 6 97-Telereglement CFONB (French Organisation for',
'52' => 'Urgent commercial payment',
'53' => 'Urgent Treasury Payment',
'54' => 'Credit card',
'55' => 'Debit card',
'56' => 'Bankgiro',
'57' => 'Standing agreement',
'58' => 'SEPA credit transfer',
'59' => 'SEPA direct debit',
'60' => 'Promissory note',
'61' => 'Promissory note signed by the debtor',
'62' => 'Promissory note signed by the debtor and endorsed by a bank',
'63' => 'Promissory note signed by the debtor and endorsed by a',
'64' => 'Promissory note signed by a bank',
'65' => 'Promissory note signed by a bank and endorsed by another',
'66' => 'Promissory note signed by a third party',
'67' => 'Promissory note signed by a third party and endorsed by a',
'68' => 'Online payment service',
'69' => 'Transfer Advice',
'70' => 'Bill drawn by the creditor on the debtor',
'74' => 'Bill drawn by the creditor on a bank',
'75' => 'Bill drawn by the creditor, endorsed by another bank',
'76' => 'Bill drawn by the creditor on a bank and endorsed by a',
'77' => 'Bill drawn by the creditor on a third party',
'78' => 'Bill drawn by creditor on third party, accepted and',
'91' => 'Not transferable banker\'s draft',
'92' => 'Not transferable local cheque',
'93' => 'Reference giro',
'94' => 'Urgent giro',
'95' => 'Free format giro',
'96' => 'Requested method for payment was not used',
'97' => 'Clearing between partners',
'ZZZ' => 'Mutually defined',
];
public string $typecode = '1';
public ?string $information = null;
public ?string $cardType = null;
public ?string $cardId = null;
public ?string $cardHolderName = null;
public ?string $buyerIban = null;
public ?string $payeeIban = null;
public ?string $payeeAccountName = null;
public ?string $payeePropId = null;
public ?string $payeeBic = null;
public function __construct(mixed $existing_payment_means = null)
{
if($existing_payment_means){
$properties = get_object_vars($this);
foreach ($properties as $property => $value) {
if (property_exists($existing_payment_means, $property)) {
$this->$property = $existing_payment_means->$property;
}
}
}
}
//requires an object which looks like this
// @param string $typecode __BT-81, From BASIC WL__ The expected or used means of payment, expressed as a code. The entries from the UNTDID 4461 code list must be used. A distinction should be made between SEPA and non-SEPA payments as well
// as between credit payments, direct debits, card payments and other means of payment
// In particular, the following codes can be used:
// * 10: cash -
// 20: check -
// 30: transfer -
// 42: Payment to bank account -
// 48: Card payment -
// 49: direct debit -
// 57: Standing order -
// 58: SEPA Credit Transfer -
// 59: SEPA Direct Debit -
// 97: Report
// *
// * @param string|null $information __BT-82, From EN 16931__ The expected or used means of payment expressed in text form, e.g. cash, bank transfer, direct debit, credit card, etc.
// * @param string|null $cardType __BT-, From __ The type of the card
// * @param string|null $cardId __BT-84, From BASIC WL__ The primary account number (PAN) to which the card used for payment belongs. In accordance with card payment security standards, an invoice should never contain a full payment card master account number.
//The following specification of the PCI Security Standards Council currently applies: The first 6 and last 4 digits at most are to be displayed
// * @param string|null $cardHolderName __BT-, From __ Name of the payment card holder
// * @param string|null $buyerIban __BT-91, From BASIC WL__ Direct debit: ID of the account to be debited
// * @param string|null $payeeIban __BT-, From __ Transfer: A unique identifier for the financial account held with a payment service provider to which the payment should be made, e.g. Use an IBAN (in the case of a SEPA payment) for a national
//ProprietaryID account number
// * @param string|null $payeeAccountName __BT-, From __ The name of the payment account held with a payment service provider to which the payment should be made. Information only required if different from the name of the payee / seller
// * @param string|null $payeePropId __BT-, From __ National account number (not for SEPA)
// * @param string|null $payeeBic __BT-, From __ Seller's banking institution, An identifier for the payment service provider with whom the payment account is managed, such as the BIC or a national bank code, if required. No identification scheme is to be used.
// *
public function run()
{
// ->getTradeSettlementPaymentMeansType($typecode, $information);
// ->getTradeSettlementFinancialCardType($cardType, $cardId, $cardHolderName);
$TradeSettlementFinancialCardType = new \horstoeko\zugferd\entities\extended\ram\TradeSettlementFinancialCardType();
$TradeSettlementFinancialCardType->setCardholderName($this->cardHolderName)
->setID(new \horstoeko\zugferd\entities\extended\udt\IDType($this->cardId));
$DebtorFinancialAccountType = new \horstoeko\zugferd\entities\extended\ram\DebtorFinancialAccountType();
$DebtorFinancialAccountType->setIBANID(new \horstoeko\zugferd\entities\extended\udt\IDType($this->buyerIban));
$CreditorFinancialAccountType = new \horstoeko\zugferd\entities\extended\ram\CreditorFinancialAccountType();
$CreditorFinancialAccountType->setAccountName($this->payeeAccountName)
->setProprietaryID(new \horstoeko\zugferd\entities\extended\udt\IDType($this->payeePropId))
->setIBANID(new \horstoeko\zugferd\entities\extended\udt\IDType($this->payeeIban));
$CreditorFinancialInstitutionType = new \horstoeko\zugferd\entities\extended\ram\CreditorFinancialInstitutionType();
$CreditorFinancialInstitutionType->setBICID(new \horstoeko\zugferd\entities\extended\udt\IDType($this->payeeBic));
$TradeSettlementPaymentMeansType = new \horstoeko\zugferd\entities\extended\ram\TradeSettlementPaymentMeansType();
$TradeSettlementPaymentMeansType->setTypeCode($this->typecode)->setInformation($this->information);
$TradeSettlementPaymentMeansType->setPayeePartyCreditorFinancialAccount($CreditorFinancialAccountType);
$TradeSettlementPaymentMeansType->setPayerPartyDebtorFinancialAccount($DebtorFinancialAccountType);
$TradeSettlementPaymentMeansType->setApplicableTradeSettlementFinancialCard($TradeSettlementFinancialCardType);
$TradeSettlementPaymentMeansType->setPayeeSpecifiedCreditorFinancialInstitution($CreditorFinancialInstitutionType);
$HeaderTradeSettlementType = new \horstoeko\zugferd\entities\extended\ram\HeaderTradeSettlementType();
$HeaderTradeSettlementType->addToSpecifiedTradeSettlementPaymentMeans($TradeSettlementPaymentMeansType);
$SupplyChainTradeTransactionType = new \horstoeko\zugferd\entities\extended\ram\SupplyChainTradeTransactionType();
$SupplyChainTradeTransactionType->setApplicableHeaderTradeSettlement($HeaderTradeSettlementType);
}
}

View File

@ -0,0 +1,108 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Services\EDocument\Adapters\UBL;
use App\Services\EDocument\Interfaces\PaymentMeansInterface;
class PaymentMeans implements PaymentMeansInterface
{
public array $payment_means_codelist = [
'1' => 'Instrument not defined',
'2' => 'Automated clearing house credit',
'3' => 'Automated clearing house debit',
'4' => 'ACH demand debit reversal',
'5' => 'ACH demand credit reversal',
'6' => 'ACH demand credit',
'7' => 'ACH demand debit',
'8' => 'Hold',
'9' => 'National or regional clearing',
'10' => 'In cash',
'11' => 'ACH savings credit reversal',
'12' => 'ACH savings debit reversal',
'13' => 'ACH savings credit',
'14' => 'ACH savings debit',
'15' => 'Bookentry credit',
'16' => 'Bookentry debit',
'17' => 'ACH demand cash concentration/disbursement (CCD) credit',
'18' => 'ACH demand cash concentration/disbursement (CCD) debit',
'19' => 'ACH demand corporate trade payment (CTP) credit',
'20' => 'Cheque',
'21' => 'Banker\'s draft',
'22' => 'Certified banker\'s draft',
'23' => 'Bank cheque (issued by a banking or similar establishment)',
'24' => 'Bill of exchange awaiting acceptance',
'25' => 'Certified cheque',
'26' => 'Local cheque',
'27' => 'ACH demand corporate trade payment (CTP) debit',
'28' => 'ACH demand corporate trade exchange (CTX) credit',
'29' => 'ACH demand corporate trade exchange (CTX) debit',
'30' => 'Credit transfer',
'31' => 'Debit transfer',
'32' => 'ACH demand cash concentration/disbursement plus (CCD+)',
'33' => 'ACH demand cash concentration/disbursement plus (CCD+)',
'34' => 'ACH prearranged payment and deposit (PPD)',
'35' => 'ACH savings cash concentration/disbursement (CCD) credit',
'36' => 'ACH savings cash concentration/disbursement (CCD) debit',
'37' => 'ACH savings corporate trade payment (CTP) credit',
'38' => 'ACH savings corporate trade payment (CTP) debit',
'39' => 'ACH savings corporate trade exchange (CTX) credit',
'40' => 'ACH savings corporate trade exchange (CTX) debit',
'41' => 'ACH savings cash concentration/disbursement plus (CCD+)',
'42' => 'Payment to bank account',
'43' => 'ACH savings cash concentration/disbursement plus (CCD+)',
'44' => 'Accepted bill of exchange',
'45' => 'Referenced home-banking credit transfer',
'46' => 'Interbank debit transfer',
'47' => 'Home-banking debit transfer',
'48' => 'Bank card',
'49' => 'Direct debit',
'50' => 'Payment by postgiro',
'51' => 'FR, norme 6 97-Telereglement CFONB (French Organisation for',
'52' => 'Urgent commercial payment',
'53' => 'Urgent Treasury Payment',
'54' => 'Credit card',
'55' => 'Debit card',
'56' => 'Bankgiro',
'57' => 'Standing agreement',
'58' => 'SEPA credit transfer',
'59' => 'SEPA direct debit',
'60' => 'Promissory note',
'61' => 'Promissory note signed by the debtor',
'62' => 'Promissory note signed by the debtor and endorsed by a bank',
'63' => 'Promissory note signed by the debtor and endorsed by a',
'64' => 'Promissory note signed by a bank',
'65' => 'Promissory note signed by a bank and endorsed by another',
'66' => 'Promissory note signed by a third party',
'67' => 'Promissory note signed by a third party and endorsed by a',
'68' => 'Online payment service',
'69' => 'Transfer Advice',
'70' => 'Bill drawn by the creditor on the debtor',
'74' => 'Bill drawn by the creditor on a bank',
'75' => 'Bill drawn by the creditor, endorsed by another bank',
'76' => 'Bill drawn by the creditor on a bank and endorsed by a',
'77' => 'Bill drawn by the creditor on a third party',
'78' => 'Bill drawn by creditor on third party, accepted and',
'91' => 'Not transferable banker\'s draft',
'92' => 'Not transferable local cheque',
'93' => 'Reference giro',
'94' => 'Urgent giro',
'95' => 'Free format giro',
'96' => 'Requested method for payment was not used',
'97' => 'Clearing between partners',
'ZZZ' => 'Mutually defined',
];
public function run()
{
}
}

View File

@ -222,7 +222,7 @@ class Storecove
}
$company_defaults = [
'acts_as_receiver' => true,
'acts_as_receiver' => false,
'acts_as_sender' => true,
'advertisements' => ['invoice'],
];

View File

@ -11,12 +11,13 @@
namespace App\Services\EDocument\Imports;
use Exception;
use App\Utils\Ninja;
use App\Models\Company;
use App\Models\Expense;
use App\Services\AbstractService;
use App\Utils\Ninja;
use Exception;
use Illuminate\Http\UploadedFile;
use App\Services\EDocument\Imports\UblEDocument;
class ParseEDocument extends AbstractService
{
@ -36,10 +37,12 @@ class ParseEDocument extends AbstractService
* @developer the function should be implemented with local first aproach to save costs of external providers (like mindee ocr)
*
* @return Expense
* @throws \Exception
* @throws \Throwable
*/
public function run(): Expense
{
nlog("starting");
nlog($this->company->id);
/** @var \App\Models\Account $account */
$account = $this->company->owner()->account;
@ -50,14 +53,23 @@ class ParseEDocument extends AbstractService
// ZUGFERD - try to parse via Zugferd lib
switch (true) {
case ($extension == 'pdf' || $mimetype == 'application/pdf'):
case ($extension == 'xml' || $mimetype == 'application/xml') && stristr($this->file->get(), "urn:cen.eu:en16931:2017"):
case ($extension == 'xml' || $mimetype == 'application/xml') && stristr($this->file->get(), "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0"):
case ($extension == 'xml' || $mimetype == 'application/xml') && stristr($this->file->get(), "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_2.1"):
case ($extension == 'xml' || $mimetype == 'application/xml') && stristr($this->file->get(), "urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_2.0"):
try {
return (new ZugferdEDocument($this->file, $this->company))->run();
} catch (Exception $e) {
} catch (\Throwable $e) {
nlog("Zugferd Exception: " . $e->getMessage());
break;
}
case ($extension == 'xml' || $mimetype == 'application/xml') && stristr($this->file->get(), "urn:cen.eu:en16931:2017"):
case ($extension == 'xml' || $mimetype == 'application/xml') && stristr($this->file->get(), "urn:oasis:names:specification:ubl"):
try {
return (new UblEDocument($this->file, $this->company))->run();
}
catch(\Throwable $e){
nlog("UBL Import Exception: " . $e->getMessage());
break;
}
}

View File

@ -0,0 +1,284 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Services\EDocument\Imports;
use App\Utils\Ninja;
use App\Utils\Number;
use App\Models\Vendor;
use App\Models\Company;
use App\Models\Country;
use App\Models\Expense;
use App\Models\Currency;
use App\Factory\VendorFactory;
use App\Factory\ExpenseFactory;
use App\Services\AbstractService;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\App;
use InvoiceNinja\EInvoice\EInvoice;
use App\Utils\Traits\SavesDocuments;
use App\Factory\VendorContactFactory;
use App\Repositories\ExpenseRepository;
use App\Services\Template\TemplateService;
class Ubl2Pdf extends AbstractService
{
/**
* @throws \Throwable
*/
public function __construct(public \InvoiceNinja\EInvoice\Models\Peppol\Invoice $invoice, public Company $company)
{
}
public function run()
{
App::forgetInstance('translator');
$t = app('translator');
App::setLocale($this->company->locale());
$t->replace(Ninja::transformTranslations($this->company->settings));
$template = file_get_contents(resource_path('views/templates/ubl/td14.html'));
// nlog($client);
// nlog($supplier);
// nlog($invoiceDetails);
// nlog($totals);
$data = [
'client' => $this->clientDetails(),
'supplier' => $this->supplierDetails(),
'invoiceDetails' => $this->invoiceDetails(),
'totals' => $this->totals(),
'metadata' => $this->metadata(),
'translations' => $this->getGenericTranslations(),
'css' => $this->customCss(),
];
$ts = new TemplateService();
$ts_instance = $ts->setCompany($this->company)
->setData($data)
->setRawTemplate($template)
->parseNinjaBlocks()
->save();
nlog($ts_instance->getHtml());
}
private function getGenericTranslations(): array
{
return [
'to' => ctrans('texts.to'),
'from' => ctrans('texts.from'),
'invoice' => ctrans('texts.invoice'),
'credit' => ctrans('texts.credit'),
'details' => ctrans('texts.details'),
'number' => ctrans('texts.number'),
'tax' => ctrans('texts.tax'),
// 'from' => ctrans('texts.from'),
// 'from' => ctrans('texts.from'),
// 'from' => ctrans('texts.from'),
// 'from' => ctrans('texts.from'),
// 'from' => ctrans('texts.from'),
// 'from' => ctrans('texts.from'),
// 'from' => ctrans('texts.from'),
// 'from' => ctrans('texts.from'),
// 'from' => ctrans('texts.from'),
// 'from' => ctrans('texts.from'),
// 'from' => ctrans('texts.from'),
];
}
private function processValues(array $array): array
{
foreach($array as $key => $value)
{
if($value === null || $value === '')
unset($array[$key]);
if($value instanceof \DateTime)
$array[$key] = $value->format($this->company->date_format());
}
return $array;
}
private function clientDetails(): array
{
return $this->processValues([
ctrans('texts.name') => data_get($this->invoice, 'AccountingCustomerParty.Party.PartyName.0.Name',''),
ctrans('texts.address1') => data_get($this->invoice, 'AccountingCustomerParty.Party.PostalAddress.StreetName',''),
ctrans('texts.address2') => data_get($this->invoice, 'AccountingCustomerParty.Party.PostalAddress.AdditionalStreetName',''),
ctrans('texts.city') => data_get($this->invoice, 'AccountingCustomerParty.Party.PostalAddress.CityName',''),
ctrans('texts.state') => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.CountrySubentity',''),
ctrans('texts.postal_code') => data_get($this->invoice, 'AccountingCustomerParty.Party.PostalAddress.PostalZone',''),
ctrans('texts.country_id') => data_get($this->invoice, 'AccountingCustomerParty.Party.PostalAddress.Country.IdentificationCode.value',''),
ctrans('texts.vat_number') => data_get($this->invoice, 'AccountingCustomerParty.Party.PartyTaxScheme.0.CompanyID.value',''),
ctrans('texts.contact_name') => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.Name',''),
ctrans('texts.phone') => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.Telephone',''),
ctrans('texts.email') => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.ElectronicMail',''),
]);
}
private function supplierDetails(): array
{
return $this->processValues([
ctrans('texts.name') => data_get($this->invoice, 'AccountingSupplierParty.Party.PartyName.0.Name', ''),
ctrans('texts.address1') => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.StreetName', ''),
ctrans('texts.address2') => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.AdditionalStreetName', ''),
ctrans('texts.city') => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.CityName', ''),
ctrans('texts.state') => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.CountrySubentity', ''),
ctrans('texts.postal_code') => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.PostalZone', ''),
ctrans('texts.country_id') => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.Country.IdentificationCode.value', ''),
ctrans('texts.routing_id') => data_get($this->invoice, 'AccountingSupplierParty.Party.EndpointID.value', ''),
ctrans('texts.id_number') => data_get($this->invoice, 'AccountingSupplierParty.Party.PartyIdentification.0.ID.value', false),
ctrans('texts.vat_number') => data_get($this->invoice, 'AccountingSupplierParty.Party.PartyTaxScheme.0.CompanyID.value', ''),
// ctrans('texts.currency_id') => $this->resolveCurrencyId(data_get($this->invoice, 'DocumentCurrencyCode.value', $this->company->currency()->code)),
ctrans('texts.contact_name') => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.Name', ''),
ctrans('texts.phone') => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.Telephone', ''),
ctrans('texts.email') => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.ElectronicMail', ''),
]);
}
private function customCss(): string
{
$css = '';
$css .= ".".str_replace(" ", "", ctrans('texts.product_key'))." { width: 15%;} ";
$css .= ".".str_replace(" ", "", ctrans('texts.quantity'))." { width: 8%;} ";
$css .= ".".str_replace(" ", "", ctrans('texts.notes'))." { width: 40%; } ";
$css .= ".".str_replace(" ", "", ctrans('texts.cost'))." { width:10%;} ";
$css .= ".".str_replace(" ", "", ctrans('texts.tax'))." { width:10%;} ";
$css .= ".".str_replace(" ", "", ctrans('texts.line_total'))." { width:15%;} ";
return $css;
}
private function invoiceDetails(): array
{
$data = $this->processValues([
ctrans('texts.currency') => data_get($this->invoice, 'DocumentCurrencyCode.value', $this->company->currency()->code),
ctrans('texts.currency_code') => data_get($this->invoice, 'InvoiceTypeCode.value', "380"),
ctrans('texts.number') => data_get($this->invoice, 'ID.value', ''),
ctrans('texts.date') => data_get($this->invoice, 'IssueDate', ''),
ctrans('texts.due_date') => data_get($this->invoice, 'DueDate', ''),
]);
$data['line_items'] = $this->invoiceLines();
return $data;
}
private function metadata(): array
{
return $this->processValues([
'currency' => data_get($this->invoice, 'DocumentCurrencyCode.value', $this->company->currency()->code),
ctrans('texts.terms') => $this->harvestTerms(),
ctrans('texts.public_notes') => data_get($this->invoice, 'Note', '')
]);
}
private function harvestTerms(): string
{
$payment_means = [];
$payment_means[] = data_get($this->invoice, 'PaymentMeans.0.PaymentMeansCode.name', false);
$payment_means[] = data_get($this->invoice, 'PaymentMeans.0.PaymentID.value', false);
$payment_means[] = data_get($this->invoice, 'PaymentMeans.0.PayeeFinancialAccount.ID.value', false);
$payment_means[] = data_get($this->invoice, 'PaymentMeans.0.PayeeFinancialAccount.Name', false);
$payment_means[] = data_get($this->invoice, 'PaymentMeans.0.PayeeFinancialAccount.FinancialInstitutionBranch.ID.value', false);
$payment_means[] = data_get($this->invoice, 'PaymentTerms.0.Note', false);
$private_notes = collect($payment_means)
->reject(function ($means) {
return $means === false;
})->implode("\n");
return $private_notes;
}
private function invoiceLines(): array
{
$lines = data_get($this->invoice, 'InvoiceLine', []);
return array_map(function ($line) {
return [
ctrans('texts.product_key') => data_get($line, 'Item.Name', ''),
// ctrans('texts.ocde') => data_get($line, 'InvoicedQuantity.UnitCode',''),
ctrans('texts.quantity') => Number::formatValue(data_get($line, 'InvoicedQuantity.amount', 0), $this->company->currency()),
ctrans('texts.notes') => data_get($line, 'Item.Description', ''),
ctrans('texts.cost') => Number::formatValue(data_get($line, 'Price.PriceAmount.amount', 0), $this->company->currency()),
'tax_name1' => data_get($line, 'Item.ClassifiedTaxCategory.0.TaxScheme.ID.value', ''),
'tax_rate1' => data_get($line, 'Item.ClassifiedTaxCategory.0.Percent', 0),
'tax_name2' => data_get($line, 'Item.ClassifiedTaxCategory.1.TaxScheme.ID.value', ''),
'tax_rate2' => data_get($line, 'Item.ClassifiedTaxCategory.1.Percent', 0),
'tax_name3' => data_get($line, 'Item.ClassifiedTaxCategory.2.TaxScheme.ID.value', ''),
'tax_rate3' => data_get($line, 'Item.ClassifiedTaxCategory.2.Percent', 0),
ctrans('texts.line_total') => Number::formatValue(data_get($line, 'LineExtensionAmount.amount', 0), $this->company->currency()),
];
}, $lines);
}
private function totals(): array
{
$tax_inc = data_get($this->invoice, 'LegalMonetaryTotal.TaxInclusiveAmount.amount', 0);
$tax_ex = data_get($this->invoice, 'LegalMonetaryTotal.TaxExclusiveAmount.amount', 0);
$tax_amount = data_get($this->invoice, 'TaxTotal.0.TaxAmount', 0);
$taxes = [];
foreach(data_get($this->invoice, 'TaxTotal.0.TaxSubtotal', []) as $tax_subtotal)
{
$taxes[] = [
'subtotal' => data_get($tax_subtotal, 'TaxAmount.amount', 0),
'tax_name' => data_get($tax_subtotal, 'TaxCategory.TaxScheme.ID.value', ''),
'tax_rate' => data_get($tax_subtotal, 'TaxCategory.Percent', 0),
];
}
return [
'subtotal' => [
ctrans('texts.subtotal') => data_get($this->invoice, 'LegalMonetaryTotal.LineExtensionAmount.amount', 0),
],
'balance' => [
ctrans('texts.balance_due') => data_get($this->invoice, 'LegalMonetaryTotal.TaxInclusiveAmount.amount', 0),
],
'taxes' => $taxes,
];
}
// private function resolveCountry(?string $iso_country_code): int
// {
// return Country::query()
// ->where('iso_3166_2', $iso_country_code)
// ->orWhere('iso_3166_3', $iso_country_code)
// ->first()?->id ?? (int)$this->company->settings->country_id;
// }
// private function resolveCurrencyId(string $currency_code): int
// {
// /** @var \Illuminate\Support\Collection<\App\Models\Currency> */
// $currencies = app('currencies');
// return $currencies->first(function (Currency $c) use ($currency_code) {
// return $c->code === $currency_code;
// })?->id ?? (int) $this->company->settings->currency_id;
// }
}

View File

@ -0,0 +1,271 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Services\EDocument\Imports;
use App\Models\Vendor;
use App\Models\Company;
use App\Models\Country;
use App\Models\Expense;
use App\Factory\VendorFactory;
use App\Factory\ExpenseFactory;
use App\Services\AbstractService;
use Illuminate\Http\UploadedFile;
use InvoiceNinja\EInvoice\EInvoice;
use App\Utils\Traits\SavesDocuments;
use App\Factory\VendorContactFactory;
use App\Models\Currency;
use App\Repositories\ExpenseRepository;
class UblEDocument extends AbstractService
{
use SavesDocuments;
/**
* @throws \Throwable
*/
public function __construct(public UploadedFile $file, public Company $company)
{
# curl -X POST http://localhost:8000/api/v1/edocument/upload -H "Content-Type: multipart/form-data" -H "X-API-TOKEN: 7tdDdkz987H3AYIWhNGXy8jTjJIoDhkAclCDLE26cTCj1KYX7EBHC66VEitJwWhn" -H "X-Requested-With: XMLHttpRequest" -F _method=PUT -F documents[]=@einvoice.xml
}
/**
* @throws \Throwable
*/
public function run(): \App\Models\Expense
{
$e = new EInvoice();
$invoice = $e->decode('Peppol', $this->file->get(), 'xml');
return $this->buildAndSaveExpense($invoice);
}
private function buildAndSaveExpense(\InvoiceNinja\EInvoice\Models\Peppol\Invoice $invoice): Expense
{
$vendor = $this->findOrCreateVendor($invoice);
$TaxExclusiveAmount = data_get($invoice, 'LegalMonetaryTotal.TaxExclusiveAmount.amount', 0);
$TaxInclusiveAmount = data_get($invoice, 'LegalMonetaryTotal.TaxExclusiveAmount.amount', 0);
$ChargeTotalAmount = data_get($invoice, 'LegalMonetaryTotal.ChargeTotalAmount.amount', 0);
$PayableAmount = data_get($invoice, 'LegalMonetaryTotal.PayableAmount.amount', 0);
$TaxAmount = data_get($invoice, 'TaxTotal.0.TaxAmount.amount', 0);
$tax_sub_totals = data_get($invoice, 'TaxTotal.0.TaxSubtotal', []);
$currency_code = data_get($invoice, 'DocumentCurrencyCode.value', $this->company->currency()->code);
$date = data_get($invoice, 'IssueDate', now()->format('Y-m-d'));
$payment_means = [];
$payment_means[] = data_get($invoice, 'PaymentMeans.0.PaymentMeansCode.name', false);
$payment_means[] = data_get($invoice, 'PaymentMeans.0.PaymentID.value', false);
$payment_means[] = data_get($invoice, 'PaymentMeans.0.PayeeFinancialAccount.ID.value', false);
$payment_means[] = data_get($invoice, 'PaymentMeans.0.PayeeFinancialAccount.Name', false);
$payment_means[] = data_get($invoice, 'PaymentMeans.0.PayeeFinancialAccount.FinancialInstitutionBranch.ID.value', false);
$payment_means[] = data_get($invoice, 'PaymentTerms.0.Note', false);
$private_notes = collect($payment_means)
->reject(function ($means){
return $means === false;
})->implode("\n");
$invoice_items = data_get($invoice, 'InvoiceLine', []);
$items = [];
foreach($invoice_items as $key => $item)
{
$items[$key]['name'] = data_get($item, 'Item.Name', false);
$items[$key]['description'] = data_get($item, 'Item.Description', false);
}
$public_notes = collect($items)->reject(function ($item){
return $item['name'] === false && $item['description'] === false;
})->map(function ($item){
return $item['name'] ?? ' ' . ' ## '. $item['description'] ?? ' '; //@phpstan-ignore-line
})->implode("\n");
/** @var \App\Models\Expense $expense */
$expense = ExpenseFactory::create($this->company->id, $this->company->owner()->id);
$expense->vendor_id = $vendor->id;
$expense->amount = $this->company->expense_inclusive_taxes ? $TaxInclusiveAmount : $TaxExclusiveAmount;
$expense->currency_id = $this->resolveCurrencyId($currency_code);
$expense->date = $date;
$expense->private_notes = $private_notes;
$expense->public_notes = $public_notes;
$tax_level = 1;
foreach ($tax_sub_totals as $key => $sub_tax) {
$amount = data_get($sub_tax, 'TaxAmount.amount', 0);
$taxable_amount = data_get($sub_tax, 'TaxableAmount.amount', 0);
$tax_rate = data_get($sub_tax, 'TaxCategory.Percent', 0);
$id = data_get($sub_tax, 'TaxCategory.ID.value', '');
$tax_name = data_get($sub_tax, 'TaxCategory.TaxScheme.ID.value', '');
if (!$this->company->calculate_expense_tax_by_amount && $tax_rate > 0) {
$expense->{"tax_rate{$tax_level}"} = $tax_rate;
$expense->{"tax_name{$tax_level}"} = $tax_name;
$expense->calculate_tax_by_amount = false;
}
else {
$expense->calculate_tax_by_amount = true;
$expense->{"tax_amount{$tax_level}"} = $amount; //@phpstan-ignore-line
}
$tax_level++;
if ($tax_level == 4) {
break;
}
}
$expense->save();
$repo = new ExpenseRepository();
$data = [];
$data['documents'][] = $this->file;
$expense = $repo->save($data, $expense);
return $expense;
}
private function resolveCurrencyId(string $currency_code): int
{
/** @var \Illuminate\Support\Collection<\App\Models\Currency> */
$currencies = app('currencies');
return $currencies->first(function (Currency $c) use ($currency_code) {
return $c->code === $currency_code;
})?->id ?? (int) $this->company->settings->currency_id;
}
private function findOrCreateVendor(\InvoiceNinja\EInvoice\Models\Peppol\Invoice $invoice): Vendor
{
$asp = $invoice->AccountingSupplierParty;
$vat_number = $this->resolveVendorVat($invoice);
$id_number = $this->resolveVendorIdNumber($invoice);
$routing_id = data_get($invoice, 'AccountingSupplierParty.Party.EndpointID.value', false);
$vendor_name = $this->resolveSupplierName($invoice);
$vendor = Vendor::query()
->where('company_id', $this->company->id)
->where(function ($q) use ($vat_number, $routing_id, $id_number, $vendor_name){
$q->when($routing_id, function ($q) use ($routing_id){
$q->where('routing_id', $routing_id);
})
->when(strlen($vat_number) > 1, function ($q) use ($vat_number){
$q->orWhere('vat_number', $vat_number);
})
->when(strlen($id_number) > 1, function ($q) use ($id_number){
$q->orWhere('id_number', $id_number);
})
->when(strlen($vendor_name) > 1, function ($q) use ($vendor_name){
$q->orWhere('name', $vendor_name);
});
})->first();
return $vendor ?? $this->newVendor($invoice);
}
private function resolveSupplierName(\InvoiceNinja\EInvoice\Models\Peppol\Invoice $invoice): string
{
if(data_get($invoice, 'AccountingSupplierParty.Party.PartyName', false)){
$party_name = data_get($invoice, 'AccountingSupplierParty.Party.PartyName', false);
return data_get($party_name[0], 'Name', '');
}
if (data_get($invoice, 'AccountingSupplierParty.Party.PartyLegalEntity', false)) {
$ple = data_get($invoice, 'AccountingSupplierParty.Party.PartyName', false);
return data_get($ple[0], 'RegistrationName', '');
}
return '';
}
private function resolveVendorIdNumber(\InvoiceNinja\EInvoice\Models\Peppol\Invoice $invoice):string
{
$pts = data_get($invoice, 'AccountingSupplierParty.Party.PartyIdentification', false);
return $pts ? data_get($pts[0], 'ID.value', '') : '';
}
private function resolveVendorVat(\InvoiceNinja\EInvoice\Models\Peppol\Invoice $invoice):string
{
$pts = data_get($invoice, 'AccountingSupplierParty.Party.PartyTaxScheme', false);
return $pts ? data_get($pts[0], 'CompanyID.value', '') : '';
}
private function newVendor(\InvoiceNinja\EInvoice\Models\Peppol\Invoice $invoice): Vendor
{
$vendor = VendorFactory::create($this->company->id, $this->company->owner()->id);
$data = [
'name' => $this->resolveSupplierName($invoice),
'routing_id' => data_get($invoice, 'AccountingSupplierParty.Party.EndpointID.value', ''),
'vat_number' => $this->resolveVendorVat($invoice),
'id_number' => $this->resolveVendorIdNumber($invoice),
'address1' => data_get($invoice, 'AccountingSupplierParty.Party.PostalAddress.StreetName',''),
'address2' => data_get($invoice, 'AccountingSupplierParty.Party.PostalAddress.AdditionalStreetName',''),
'city' => data_get($invoice, 'AccountingSupplierParty.Party.PostalAddress.CityName',''),
'state' => data_get($invoice, 'AccountingSupplierParty.Party.PostalAddress.CountrySubentity',''),
'postal_code' => data_get($invoice, 'AccountingSupplierParty.Party.PostalAddress.PostalZone',''),
'country_id' => $this->resolveCountry(data_get($invoice, 'AccountingSupplierParty.Party.PostalAddress.Country.IdentificationCode.value','')),
'currency_id' => $this->resolveCurrencyId(data_get($invoice, 'DocumentCurrencyCode.value', $this->company->currency()->code)),
];
$vendor->fill($data);
$vendor->save();
$vendor->service()->applyNumber();
if(data_get($invoice, 'AccountingSupplierParty.Party.Contact',false))
{
$vc = VendorContactFactory::create($this->company->id, $this->company->owner()->id);
$vc->vendor_id = $vendor->id;
$vc->first_name = data_get($invoice, 'AccountingSupplierParty.Party.Contact.Name','');
$vc->phone = data_get($invoice, 'AccountingSupplierParty.Party.Contact.Telephone', '');
$vc->email = data_get($invoice, 'AccountingSupplierParty.Party.Contact.ElectronicMail', '');
$vc->save();
}
return $vendor->fresh();
}
private function resolveCountry(?string $iso_country_code): int
{
return Country::query()
->where('iso_3166_2', $iso_country_code)
->orWhere('iso_3166_3', $iso_country_code)
->first()?->id ?? (int)$this->company->settings->country_id;
}
}

View File

@ -24,6 +24,9 @@ use App\Utils\TempFile;
use App\Utils\Traits\SavesDocuments;
use Exception;
use App\Models\Company;
use App\Repositories\ExpenseRepository;
use App\Repositories\VendorContactRepository;
use App\Repositories\VendorRepository;
use horstoeko\zugferd\ZugferdDocumentReader;
use horstoeko\zugferdvisualizer\ZugferdVisualizer;
use horstoeko\zugferdvisualizer\renderer\ZugferdVisualizerLaravelRenderer;
@ -90,6 +93,7 @@ class ZugferdEDocument extends AbstractService
$this->document->getDocumentTax($categoryCode, $typeCode, $basisAmount, $calculatedAmount, $rateApplicablePercent, $exemptionReason, $exemptionReasonCode, $lineTotalBasisAmount, $allowanceChargeBasisAmount, $taxPointDate, $dueDateTypeCode);
$expense->{"tax_amount$counter"} = $calculatedAmount;
$expense->{"tax_rate$counter"} = $rateApplicablePercent;
$expense->{"tax_name$counter"} = $typeCode;
$counter++;
} while ($this->document->nextDocumentTax());
}
@ -137,17 +141,22 @@ class ZugferdEDocument extends AbstractService
if ($country)
$vendor->country_id = $country->id;
$vendor->save();
$vendor_repo = new VendorRepository(new VendorContactRepository());
$vendor = $vendor_repo->save([], $vendor);
$expense->vendor_id = $vendor->id;
}
$expense->transaction_reference = $documentno;
} else {
// The document exists as an expense
// Handle accordingly
nlog("Zugferd: Document already exists");
nlog("Zugferd: Document already exists {$expense->hashed_id}");
$expense->private_notes = $expense->private_notes . ctrans("texts.edocument_import_already_exists", ["date" => time()]);
}
$expense->save();
$expense_repo = new ExpenseRepository();
$expense = $expense_repo->save([],$expense);
return $expense;
}
}

View File

@ -0,0 +1,17 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Services\EDocument\Interfaces;
interface PaymentMeansInterface
{
public function run();
}

View File

@ -992,7 +992,6 @@ class Peppol extends AbstractService
$physical_location->Address = $address;
$party->PhysicalLocation = $physical_location;
;
$contact = new Contact();
$contact->ElectronicMail = $this->invoice->client->present()->email();

View File

@ -622,7 +622,7 @@ class Email implements ShouldQueue
$company = $this->company;
$smtp_host = $company->smtp_host ?? '';
$smtp_port = $company->smtp_port;
$smtp_port = (int)$company->smtp_port;
$smtp_username = $company->smtp_username ?? '';
$smtp_password = $company->smtp_password ?? '';
$smtp_encryption = $company->smtp_encryption ?? 'tls';

View File

@ -174,8 +174,19 @@ class AutoBillInvoice extends AbstractService
$this->invoice->auto_bill_tries += 1;
if ($this->invoice->auto_bill_tries == 3) {
$this->invoice->auto_bill_enabled = false;
$this->invoice->auto_bill_tries = 0; //reset the counter here in case auto billing is turned on again in the future.
\App\Models\Invoice::where('id', $this->invoice->id)->update([
'auto_bill_enabled' => false,
'auto_bill_tries' => 0
]);
}
else {
\App\Models\Invoice::where('id', $this->invoice->id)->update([
'auto_bill_tries' => $this->invoice->auto_bill_tries
]);
}
if ($payment) {
@ -347,6 +358,8 @@ class AutoBillInvoice extends AbstractService
$payload = ['client_id' => $this->invoice->client_id, 'invoices' => [['invoice_id' => $this->invoice->id,'amount' => $payment_balance]]];
$payment_repo->save($payload, $payment);
$this->invoice = $this->invoice->fresh();
}
}

View File

@ -94,7 +94,31 @@ class DeletePayment
nlog("net deletable amount - refunded = {$net_deletable}");
if (! $paymentable_invoice->is_deleted) {
if($paymentable_invoice->status_id == Invoice::STATUS_CANCELLED){
$is_trashed = false;
if($paymentable_invoice->trashed()){
$is_trashed = true;
$paymentable_invoice->restore();
}
$paymentable_invoice->service()
->updatePaidToDate($net_deletable * -1)
->save();
$this->payment
->client
->service()
->updatePaidToDate($net_deletable * -1)
->save();
if($is_trashed){
$paymentable_invoice->delete();
}
}
elseif (! $paymentable_invoice->is_deleted) {
$paymentable_invoice->restore();
$paymentable_invoice->service()
@ -133,11 +157,16 @@ class DeletePayment
//sometimes the payment is NOT created properly, this catches the payment and prevents the paid to date reducing inappropriately.
if ($this->update_client_paid_to_date) {
$reduced_paid_to_date = $this->payment->amount < 0 ? $this->payment->amount * -1 : min(0, ($this->payment->amount - $this->payment->refunded - $this->_paid_to_date_deleted) * -1);
// $reduced_paid_to_date = min(0, ($this->payment->amount - $this->payment->refunded - $this->_paid_to_date_deleted) * -1);
$this->payment
->client
->service()
->updatePaidToDate(min(0, ($this->payment->amount - $this->payment->refunded - $this->_paid_to_date_deleted) * -1))
->save();
->client
->service()
->updatePaidToDate($reduced_paid_to_date)
->save();
}
return $this;

View File

@ -88,6 +88,7 @@ class PdfService
*/
public function getPdf()
{
try {
$pdf = $this->resolvePdfEngine($this->getHtml());

View File

@ -120,116 +120,116 @@ class QuickbooksImport implements ShouldQueue
};
}
private function syncQbToNinjaInvoices($records): void
{
// private function syncQbToNinjaInvoices($records): void
// {
}
// }
private function syncQbToNinjaVendors(array $records): void
{
// private function syncQbToNinjaVendors(array $records): void
// {
$transformer = new VendorTransformer($this->company);
// $transformer = new VendorTransformer($this->company);
foreach($records as $record)
{
$ninja_data = $transformer->qbToNinja($record);
// foreach($records as $record)
// {
// $ninja_data = $transformer->qbToNinja($record);
if($vendor = $this->findVendor($ninja_data))
{
$vendor->fill($ninja_data[0]);
$vendor->saveQuietly();
// if($vendor = $this->findVendor($ninja_data))
// {
// $vendor->fill($ninja_data[0]);
// $vendor->saveQuietly();
$contact = $vendor->contacts()->where('email', $ninja_data[1]['email'])->first();
// $contact = $vendor->contacts()->where('email', $ninja_data[1]['email'])->first();
if(!$contact)
{
$contact = VendorContactFactory::create($this->company->id, $this->company->owner()->id);
$contact->vendor_id = $vendor->id;
$contact->send_email = true;
$contact->is_primary = true;
$contact->fill($ninja_data[1]);
$contact->saveQuietly();
}
elseif($this->qbs->syncable('vendor', \App\Enum\SyncDirection::PULL)){
$contact->fill($ninja_data[1]);
$contact->saveQuietly();
}
// if(!$contact)
// {
// $contact = VendorContactFactory::create($this->company->id, $this->company->owner()->id);
// $contact->vendor_id = $vendor->id;
// $contact->send_email = true;
// $contact->is_primary = true;
// $contact->fill($ninja_data[1]);
// $contact->saveQuietly();
// }
// elseif($this->qbs->syncable('vendor', \App\Enum\SyncDirection::PULL)){
// $contact->fill($ninja_data[1]);
// $contact->saveQuietly();
// }
}
// }
}
}
// }
// }
private function syncQbToNinjaExpenses(array $records): void
{
// private function syncQbToNinjaExpenses(array $records): void
// {
$transformer = new ExpenseTransformer($this->company);
// $transformer = new ExpenseTransformer($this->company);
foreach($records as $record)
{
$ninja_data = $transformer->qbToNinja($record);
// foreach($records as $record)
// {
// $ninja_data = $transformer->qbToNinja($record);
if($expense = $this->findExpense($ninja_data))
{
$expense->fill($ninja_data);
$expense->saveQuietly();
}
// if($expense = $this->findExpense($ninja_data))
// {
// $expense->fill($ninja_data);
// $expense->saveQuietly();
// }
}
}
// }
// }
private function findExpense(array $qb_data): ?Expense
{
$expense = $qb_data;
// private function findExpense(array $qb_data): ?Expense
// {
// $expense = $qb_data;
$search = Expense::query()
->withTrashed()
->where('company_id', $this->company->id)
->where('number', $expense['number']);
// $search = Expense::query()
// ->withTrashed()
// ->where('company_id', $this->company->id)
// ->where('number', $expense['number']);
if($search->count() == 0) {
return ExpenseFactory::create($this->company->id, $this->company->owner()->id);
}
elseif($search->count() == 1) {
return $this->qbs->syncable('expense', \App\Enum\SyncDirection::PULL) ? $search->first() : null;
}
// if($search->count() == 0) {
// return ExpenseFactory::create($this->company->id, $this->company->owner()->id);
// }
// elseif($search->count() == 1) {
// return $this->qbs->syncable('expense', \App\Enum\SyncDirection::PULL) ? $search->first() : null;
// }
return null;
}
// return null;
// }
private function findVendor(array $qb_data) :?Vendor
{
$vendor = $qb_data[0];
$contact = $qb_data[1];
$vendor_meta = $qb_data[2];
// private function findVendor(array $qb_data) :?Vendor
// {
// $vendor = $qb_data[0];
// $contact = $qb_data[1];
// $vendor_meta = $qb_data[2];
$search = Vendor::query()
->withTrashed()
->where('company_id', $this->company->id)
->where(function ($q) use ($vendor, $vendor_meta, $contact){
// $search = Vendor::query()
// ->withTrashed()
// ->where('company_id', $this->company->id)
// ->where(function ($q) use ($vendor, $vendor_meta, $contact){
$q->where('vendor_hash', $vendor_meta['vendor_hash'])
->orWhere('number', $vendor['number'])
->orWhereHas('contacts', function ($q) use ($contact){
$q->where('email', $contact['email']);
});
// $q->where('vendor_hash', $vendor_meta['vendor_hash'])
// ->orWhere('number', $vendor['number'])
// ->orWhereHas('contacts', function ($q) use ($contact){
// $q->where('email', $contact['email']);
// });
});
// });
if($search->count() == 0) {
//new client
return VendorFactory::create($this->company->id, $this->company->owner()->id);
}
elseif($search->count() == 1) {
// if($search->count() == 0) {
// //new client
// return VendorFactory::create($this->company->id, $this->company->owner()->id);
// }
// elseif($search->count() == 1) {
return $this->qbs->syncable('vendor', \App\Enum\SyncDirection::PULL) ? $search->first() : null;
}
// return $this->qbs->syncable('vendor', \App\Enum\SyncDirection::PULL) ? $search->first() : null;
// }
return null;
}
// return null;
// }
public function middleware()
{

View File

@ -11,38 +11,41 @@
namespace App\Services\Template;
use App\Models\Task;
use App\Models\User;
use App\Models\Quote;
use App\Utils\Number;
use Twig\Error\Error;
use App\Models\Client;
use App\Models\Company;
use App\Models\Credit;
use App\Models\Design;
use App\Models\Vendor;
use App\Models\Company;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Project;
use App\Models\PurchaseOrder;
use App\Models\Quote;
use App\Models\RecurringInvoice;
use App\Models\User;
use App\Models\Vendor;
use App\Utils\HostedPDF\NinjaPdf;
use App\Utils\HtmlEngine;
use App\Utils\Number;
use Twig\Error\LoaderError;
use Twig\Error\SyntaxError;
use Twig\Error\RuntimeError;
use App\Models\PurchaseOrder;
use App\Utils\Traits\MakesHash;
use App\Utils\VendorHtmlEngine;
use Twig\Sandbox\SecurityError;
use App\Models\RecurringInvoice;
use App\Utils\PaymentHtmlEngine;
use App\Utils\Traits\MakesDates;
use App\Utils\HostedPDF\NinjaPdf;
use App\Utils\Traits\Pdf\PdfMaker;
use App\Utils\VendorHtmlEngine;
use League\CommonMark\CommonMarkConverter;
use Twig\Error\Error;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use Twig\Extra\Intl\IntlExtension;
use Twig\Sandbox\SecurityError;
use League\CommonMark\CommonMarkConverter;
class TemplateService
{
use MakesDates;
use PdfMaker;
use MakesHash;
private \DomDocument $document;
public \Twig\Environment $twig;
@ -61,7 +64,7 @@ class TemplateService
private ?Vendor $vendor = null;
private Invoice | Quote | Credit | PurchaseOrder | RecurringInvoice $entity;
private Invoice | Quote | Credit | PurchaseOrder | RecurringInvoice | Task | Project $entity;
private Payment $payment;
@ -92,7 +95,7 @@ class TemplateService
$loader = new \Twig\Loader\FilesystemLoader(storage_path());
$this->twig = new \Twig\Environment($loader, [
'debug' => true,
'debug' => true,
]);
$string_extension = new \Twig\Extension\StringLoaderExtension();
@ -100,7 +103,6 @@ class TemplateService
$this->twig->addExtension(new IntlExtension());
$this->twig->addExtension(new \Twig\Extension\DebugExtension());
$function = new \Twig\TwigFunction('img', function ($string, $style = '') {
return '<img src="' . $string . '" style="' . $style . '"></img>';
});
@ -124,7 +126,7 @@ class TemplateService
$this->twig->addFilter($filter);
$allowedTags = ['if', 'for', 'set', 'filter'];
$allowedFilters = ['replace', 'escape', 'e', 'upper', 'lower', 'capitalize', 'filter', 'length', 'merge','format_currency', 'format_number','format_percent_number','map', 'join', 'first', 'date', 'sum', 'number_format','nl2br'];
$allowedFilters = ['replace', 'escape', 'e', 'upper', 'lower', 'capitalize', 'filter', 'length', 'merge','format_currency', 'format_number','format_percent_number','map', 'join', 'first', 'date', 'sum', 'number_format','nl2br','striptags'];
$allowedFunctions = ['range', 'cycle', 'constant', 'date',];
$allowedProperties = ['type_id'];
$allowedMethods = ['img','t'];
@ -292,7 +294,7 @@ class TemplateService
*
* @return self
*/
private function parseNinjaBlocks(): self
public function parseNinjaBlocks(): self
{
$replacements = [];
@ -350,6 +352,12 @@ class TemplateService
return $this;
}
public function setData(array $data): self
{
$this->data = $data;
return $this;
}
/**
* Parses all variables in the document
*
@ -379,7 +387,7 @@ class TemplateService
*
* @return self
*/
private function save(): self
public function save(): self
{
$this->compiled_html = str_replace('%24', '$', $this->document->saveHTML());
@ -409,6 +417,14 @@ class TemplateService
}
public function setRawTemplate(string $template):self
{
@$this->document->loadHTML(mb_convert_encoding($template, 'HTML-ENTITIES', 'UTF-8'));
return $this;
}
/**
* Inject the template components
* manually
@ -612,7 +628,8 @@ class TemplateService
$item->gross_line_total = Number::formatMoney($item->gross_line_total_raw, $client_or_vendor);
$item->tax_amount = Number::formatMoney($item->tax_amount_raw, $client_or_vendor);
$item->product_cost = Number::formatMoney($item->product_cost_raw, $client_or_vendor);
$item->task = strlen($item->task_id ?? '') > 1 ? $this->processInvoiceTask($item->task_id) : [];
return (array)$item;
})->toArray();
@ -929,6 +946,35 @@ class TemplateService
'locale' => substr($entity->client->locale(), 0, 2),
] : [];
}
private function processInvoiceTask(string $task_id): array
{
$task = Task::where('company_id', $this->company->id)
->where('id', $this->decodePrimaryKey($task_id))
->first();
return $task ? [
'number' => (string) $task->number ?: '',
'description' => (string) $task->description ?: '',
'duration' => $task->calcDuration() ?: 0,
'rate' => Number::formatMoney($task->rate ?? 0, $task->client ?? $task->company),
'rate_raw' => $task->rate ?? 0,
'created_at' => $this->translateDate($task->created_at, $task->client ? $task->client->date_format() : $task->company->date_format(), $task->client ? $task->client->locale() : $task->company->locale()),
'updated_at' => $this->translateDate($task->updated_at, $task->client ? $task->client->date_format() : $task->company->date_format(), $task->client ? $task->client->locale() : $task->company->locale()),
'date' => $task->calculated_start_date ? $this->translateDate($task->calculated_start_date, $task->client ? $task->client->date_format() : $task->company->date_format(), $task->client ? $task->client->locale() : $task->company->locale()) : '',
'project' => $task->project ? $this->transformProject($task->project, true) : [],
'time_log' => $task->processLogsExpandedNotation(),
'custom_value1' => $task->custom_value1 ?: '',
'custom_value2' => $task->custom_value2 ?: '',
'custom_value3' => $task->custom_value3 ?: '',
'custom_value4' => $task->custom_value4 ?: '',
'status' => $task->status ? $task->status->name : '',
'user' => $this->userInfo($task->user),
'assigned_user' => $task->assigned_user ? $this->userInfo($task->assigned_user) : [],
] : [];
}
/**
* @todo refactor
*
@ -1311,19 +1357,21 @@ class TemplateService
*/
private function resolveEntity(): string
{
$entity_string = '';
//@phpstan-ignore-next-line
match($this->entity) {
($this->entity instanceof Invoice) => $entity_string = 'invoice',
($this->entity instanceof Quote) => $entity_string = 'quote',
($this->entity instanceof Credit) => $entity_string = 'credit',
($this->entity instanceof RecurringInvoice) => $entity_string = 'invoice',
($this->entity instanceof PurchaseOrder) => $entity_string = 'purchase_order',
default => $entity_string = 'invoice',
};
return $entity_string;
switch ($this->entity) {
case ($this->entity instanceof Invoice):
return 'invoice';
case ($this->entity instanceof Quote):
return 'quote';
case ($this->entity instanceof Credit):
return 'credit';
case ($this->entity instanceof RecurringInvoice):
return 'invoice';
case ($this->entity instanceof PurchaseOrder):
return 'purchase_order';
default:
return 'invoice';
}
}

View File

@ -16,6 +16,7 @@ use App\Models\Document;
use App\Models\Expense;
use App\Models\ExpenseCategory;
use App\Models\Invoice;
use App\Models\Project;
use App\Models\Vendor;
use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\SoftDeletes;
@ -41,6 +42,7 @@ class ExpenseTransformer extends EntityTransformer
'vendor',
'category',
'invoice',
'project',
];
public function includeDocuments(Expense $expense)
@ -50,6 +52,17 @@ class ExpenseTransformer extends EntityTransformer
return $this->includeCollection($expense->documents, $transformer, Document::class);
}
public function includeProject(Expense $expense): ?Item
{
$transformer = new ProjectTransformer($this->serializer);
if (!$expense->project) {
return null;
}
return $this->includeItem($expense->project, $transformer, Project::class);
}
public function includeClient(Expense $expense): ?Item
{
$transformer = new ClientTransformer($this->serializer);

View File

@ -830,7 +830,7 @@ class HtmlEngine
}
}
if (!$this->entity->company->tax_data->regions->EU->has_sales_above_threshold ?? false){
if (!$this->entity->company->tax_data->regions->EU->has_sales_above_threshold ?? false){ //@phpstan-ignore-line
$tax_label .= ctrans('text.small_company_info') ."<br>";
}

View File

@ -124,6 +124,8 @@ class Statics
$data['bulk_updates'] = [
'client' => \App\Models\Client::$bulk_update_columns,
'expense' => \App\Models\Expense::$bulk_update_columns,
'recurring_invoice' => \App\Models\RecurringInvoice::$bulk_update_columns,
];
return $data;

View File

@ -12,12 +12,13 @@
namespace App\Utils\Traits\Invoice\Broadcasting;
use App\Models\BaseModel;
use App\Transformers\ArraySerializer;
use Illuminate\Broadcasting\PrivateChannel;
use League\Fractal\Manager;
use League\Fractal\Resource\Item;
trait DefaultInvoiceBroadcast
trait DefaultResourceBroadcast
{
public function broadcastOn(): array
{
@ -26,17 +27,40 @@ trait DefaultInvoiceBroadcast
];
}
public function broadcastModel(): BaseModel
{
throw new \LogicException('Make sure to pass a model to the broadcastModel method.');
}
public function broadcastManager(Manager $manager): Manager
{
return $manager;
}
public function broadcastWith(): array
{
$entity = $this->broadcastModel();
$manager = new Manager();
$manager->setSerializer(new ArraySerializer());
$class = sprintf('App\\Transformers\\%sTransformer', class_basename($this->invoice));
$class = sprintf('App\\Transformers\\%sTransformer', class_basename($entity));
$transformer = new $class();
$resource = new Item($this->invoice, $transformer, $this->invoice->getEntityType());
$resource = new Item($entity, $transformer, $entity->getEntityType());
$manager = $this->broadcastManager($manager);
$data = $manager->createData($resource)->toArray();
return [...$data, 'x-socket-id' => $this->socket];
$data = [...$data];
if (\property_exists($this, 'socket')) {
$data['x-socket-id'] = $this->socket;
}
return $data;
}
}

237
composer.lock generated
View File

@ -535,16 +535,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.323.2",
"version": "3.324.1",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "030b93340f0e7d6fd20276037087d3eab8f16575"
"reference": "5b824a9b8015a38f18c53b023975c0f63c7bd3dc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/030b93340f0e7d6fd20276037087d3eab8f16575",
"reference": "030b93340f0e7d6fd20276037087d3eab8f16575",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/5b824a9b8015a38f18c53b023975c0f63c7bd3dc",
"reference": "5b824a9b8015a38f18c53b023975c0f63c7bd3dc",
"shasum": ""
},
"require": {
@ -627,9 +627,9 @@
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.323.2"
"source": "https://github.com/aws/aws-sdk-php/tree/3.324.1"
},
"time": "2024-10-07T18:11:24+00:00"
"time": "2024-10-11T18:22:01+00:00"
},
{
"name": "babenkoivan/elastic-adapter",
@ -1580,16 +1580,16 @@
},
{
"name": "doctrine/dbal",
"version": "4.1.1",
"version": "4.2.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/dbal.git",
"reference": "7a8252418689feb860ea8dfeab66d64a56a64df8"
"reference": "dadd35300837a3a2184bd47d403333b15d0a9bd0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/7a8252418689feb860ea8dfeab66d64a56a64df8",
"reference": "7a8252418689feb860ea8dfeab66d64a56a64df8",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/dadd35300837a3a2184bd47d403333b15d0a9bd0",
"reference": "dadd35300837a3a2184bd47d403333b15d0a9bd0",
"shasum": ""
},
"require": {
@ -1602,7 +1602,7 @@
"doctrine/coding-standard": "12.0.0",
"fig/log-test": "^1",
"jetbrains/phpstorm-stubs": "2023.2",
"phpstan/phpstan": "1.12.0",
"phpstan/phpstan": "1.12.6",
"phpstan/phpstan-phpunit": "1.4.0",
"phpstan/phpstan-strict-rules": "^1.6",
"phpunit/phpunit": "10.5.30",
@ -1668,7 +1668,7 @@
],
"support": {
"issues": "https://github.com/doctrine/dbal/issues",
"source": "https://github.com/doctrine/dbal/tree/4.1.1"
"source": "https://github.com/doctrine/dbal/tree/4.2.1"
},
"funding": [
{
@ -1684,7 +1684,7 @@
"type": "tidelift"
}
],
"time": "2024-09-03T08:58:39+00:00"
"time": "2024-10-10T18:01:27+00:00"
},
{
"name": "doctrine/deprecations",
@ -2035,16 +2035,16 @@
},
{
"name": "dragonmantank/cron-expression",
"version": "v3.3.3",
"version": "v3.4.0",
"source": {
"type": "git",
"url": "https://github.com/dragonmantank/cron-expression.git",
"reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a"
"reference": "8c784d071debd117328803d86b2097615b457500"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/adfb1f505deb6384dc8b39804c5065dd3c8c8c0a",
"reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a",
"url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500",
"reference": "8c784d071debd117328803d86b2097615b457500",
"shasum": ""
},
"require": {
@ -2057,10 +2057,14 @@
"require-dev": {
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^1.0",
"phpstan/phpstan-webmozart-assert": "^1.0",
"phpunit/phpunit": "^7.0|^8.0|^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Cron\\": "src/Cron/"
@ -2084,7 +2088,7 @@
],
"support": {
"issues": "https://github.com/dragonmantank/cron-expression/issues",
"source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.3"
"source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0"
},
"funding": [
{
@ -2092,7 +2096,7 @@
"type": "github"
}
],
"time": "2023-08-10T19:36:49+00:00"
"time": "2024-10-09T13:47:03+00:00"
},
{
"name": "egulias/email-validator",
@ -3882,16 +3886,16 @@
},
{
"name": "horstoeko/zugferd",
"version": "v1.0.65",
"version": "v1.0.66",
"source": {
"type": "git",
"url": "https://github.com/horstoeko/zugferd.git",
"reference": "ccafcda546f6867b3203420331fd436baf028187"
"reference": "2a009cd78d6aab7771f2a6dbf31c8c0cfd4f3fc5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/horstoeko/zugferd/zipball/ccafcda546f6867b3203420331fd436baf028187",
"reference": "ccafcda546f6867b3203420331fd436baf028187",
"url": "https://api.github.com/repos/horstoeko/zugferd/zipball/2a009cd78d6aab7771f2a6dbf31c8c0cfd4f3fc5",
"reference": "2a009cd78d6aab7771f2a6dbf31c8c0cfd4f3fc5",
"shasum": ""
},
"require": {
@ -3951,9 +3955,9 @@
],
"support": {
"issues": "https://github.com/horstoeko/zugferd/issues",
"source": "https://github.com/horstoeko/zugferd/tree/v1.0.65"
"source": "https://github.com/horstoeko/zugferd/tree/v1.0.66"
},
"time": "2024-10-06T09:56:35+00:00"
"time": "2024-10-11T09:37:47+00:00"
},
{
"name": "horstoeko/zugferdvisualizer",
@ -4883,16 +4887,16 @@
},
{
"name": "laravel/framework",
"version": "v11.26.0",
"version": "v11.27.2",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "b8cb8998701d5b3cfe68539d3c3da1fc59ddd82b"
"reference": "a51d1f2b771c542324a3d9b76a98b1bbc75c0ee9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/b8cb8998701d5b3cfe68539d3c3da1fc59ddd82b",
"reference": "b8cb8998701d5b3cfe68539d3c3da1fc59ddd82b",
"url": "https://api.github.com/repos/laravel/framework/zipball/a51d1f2b771c542324a3d9b76a98b1bbc75c0ee9",
"reference": "a51d1f2b771c542324a3d9b76a98b1bbc75c0ee9",
"shasum": ""
},
"require": {
@ -5088,7 +5092,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2024-10-01T14:29:34+00:00"
"time": "2024-10-09T04:17:35+00:00"
},
{
"name": "laravel/prompts",
@ -5620,38 +5624,38 @@
},
{
"name": "lcobucci/jwt",
"version": "5.3.0",
"version": "5.4.0",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/jwt.git",
"reference": "08071d8d2c7f4b00222cc4b1fb6aa46990a80f83"
"reference": "aac4fd512681fd5cb4b77d2105ab7ec700c72051"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/08071d8d2c7f4b00222cc4b1fb6aa46990a80f83",
"reference": "08071d8d2c7f4b00222cc4b1fb6aa46990a80f83",
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/aac4fd512681fd5cb4b77d2105ab7ec700c72051",
"reference": "aac4fd512681fd5cb4b77d2105ab7ec700c72051",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"ext-sodium": "*",
"php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"php": "~8.2.0 || ~8.3.0 || ~8.4.0",
"psr/clock": "^1.0"
},
"require-dev": {
"infection/infection": "^0.27.0",
"lcobucci/clock": "^3.0",
"infection/infection": "^0.29",
"lcobucci/clock": "^3.2",
"lcobucci/coding-standard": "^11.0",
"phpbench/phpbench": "^1.2.9",
"phpbench/phpbench": "^1.2",
"phpstan/extension-installer": "^1.2",
"phpstan/phpstan": "^1.10.7",
"phpstan/phpstan-deprecation-rules": "^1.1.3",
"phpstan/phpstan-phpunit": "^1.3.10",
"phpstan/phpstan-strict-rules": "^1.5.0",
"phpunit/phpunit": "^10.2.6"
"phpunit/phpunit": "^11.1"
},
"suggest": {
"lcobucci/clock": ">= 3.0"
"lcobucci/clock": ">= 3.2"
},
"type": "library",
"autoload": {
@ -5677,7 +5681,7 @@
],
"support": {
"issues": "https://github.com/lcobucci/jwt/issues",
"source": "https://github.com/lcobucci/jwt/tree/5.3.0"
"source": "https://github.com/lcobucci/jwt/tree/5.4.0"
},
"funding": [
{
@ -5689,7 +5693,7 @@
"type": "patreon"
}
],
"time": "2024-04-11T23:07:54+00:00"
"time": "2024-10-08T22:06:45+00:00"
},
{
"name": "league/commonmark",
@ -5881,16 +5885,16 @@
},
{
"name": "league/csv",
"version": "9.16.0",
"version": "9.17.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/csv.git",
"reference": "998280c6c34bd67d8125fdc8b45bae28d761b440"
"reference": "8cab815fb11ec93aa2f7b8a57b3daa1f1a364011"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/998280c6c34bd67d8125fdc8b45bae28d761b440",
"reference": "998280c6c34bd67d8125fdc8b45bae28d761b440",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/8cab815fb11ec93aa2f7b8a57b3daa1f1a364011",
"reference": "8cab815fb11ec93aa2f7b8a57b3daa1f1a364011",
"shasum": ""
},
"require": {
@ -5898,17 +5902,16 @@
"php": "^8.1.2"
},
"require-dev": {
"doctrine/collections": "^2.2.2",
"ext-dom": "*",
"ext-xdebug": "*",
"friendsofphp/php-cs-fixer": "^3.57.1",
"phpbench/phpbench": "^1.2.15",
"phpstan/phpstan": "^1.11.1",
"phpstan/phpstan-deprecation-rules": "^1.2.0",
"friendsofphp/php-cs-fixer": "^3.64.0",
"phpbench/phpbench": "^1.3.1",
"phpstan/phpstan": "^1.12.5",
"phpstan/phpstan-deprecation-rules": "^1.2.1",
"phpstan/phpstan-phpunit": "^1.4.0",
"phpstan/phpstan-strict-rules": "^1.6.0",
"phpunit/phpunit": "^10.5.16 || ^11.1.3",
"symfony/var-dumper": "^6.4.6 || ^7.0.7"
"phpstan/phpstan-strict-rules": "^1.6.1",
"phpunit/phpunit": "^10.5.16 || ^11.4.0",
"symfony/var-dumper": "^6.4.8 || ^7.1.5"
},
"suggest": {
"ext-dom": "Required to use the XMLConverter and the HTMLConverter classes",
@ -5926,7 +5929,7 @@
"src/functions_include.php"
],
"psr-4": {
"League\\Csv\\": "src"
"League\\Csv\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -5965,20 +5968,20 @@
"type": "github"
}
],
"time": "2024-05-24T11:04:54+00:00"
"time": "2024-10-10T10:30:28+00:00"
},
{
"name": "league/flysystem",
"version": "3.29.0",
"version": "3.29.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
"reference": "0adc0d9a51852e170e0028a60bd271726626d3f0"
"reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/0adc0d9a51852e170e0028a60bd271726626d3f0",
"reference": "0adc0d9a51852e170e0028a60bd271726626d3f0",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/edc1bb7c86fab0776c3287dbd19b5fa278347319",
"reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319",
"shasum": ""
},
"require": {
@ -6046,9 +6049,9 @@
],
"support": {
"issues": "https://github.com/thephpleague/flysystem/issues",
"source": "https://github.com/thephpleague/flysystem/tree/3.29.0"
"source": "https://github.com/thephpleague/flysystem/tree/3.29.1"
},
"time": "2024-09-29T11:59:11+00:00"
"time": "2024-10-08T08:58:34+00:00"
},
{
"name": "league/flysystem-aws-s3-v3",
@ -6358,16 +6361,16 @@
},
{
"name": "livewire/livewire",
"version": "v3.5.9",
"version": "v3.5.10",
"source": {
"type": "git",
"url": "https://github.com/livewire/livewire.git",
"reference": "d04a229058afa76116d0e39209943a8ea3a7f888"
"reference": "774092003edb2670615ef09f3a9fbdd335d6d0d7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/livewire/livewire/zipball/d04a229058afa76116d0e39209943a8ea3a7f888",
"reference": "d04a229058afa76116d0e39209943a8ea3a7f888",
"url": "https://api.github.com/repos/livewire/livewire/zipball/774092003edb2670615ef09f3a9fbdd335d6d0d7",
"reference": "774092003edb2670615ef09f3a9fbdd335d6d0d7",
"shasum": ""
},
"require": {
@ -6422,7 +6425,7 @@
"description": "A front-end framework for Laravel.",
"support": {
"issues": "https://github.com/livewire/livewire/issues",
"source": "https://github.com/livewire/livewire/tree/v3.5.9"
"source": "https://github.com/livewire/livewire/tree/v3.5.10"
},
"funding": [
{
@ -6430,20 +6433,20 @@
"type": "github"
}
],
"time": "2024-10-01T12:40:06+00:00"
"time": "2024-10-10T20:03:38+00:00"
},
{
"name": "maennchen/zipstream-php",
"version": "3.1.0",
"version": "3.1.1",
"source": {
"type": "git",
"url": "https://github.com/maennchen/ZipStream-PHP.git",
"reference": "b8174494eda667f7d13876b4a7bfef0f62a7c0d1"
"reference": "6187e9cc4493da94b9b63eb2315821552015fca9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/b8174494eda667f7d13876b4a7bfef0f62a7c0d1",
"reference": "b8174494eda667f7d13876b4a7bfef0f62a7c0d1",
"url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/6187e9cc4493da94b9b63eb2315821552015fca9",
"reference": "6187e9cc4493da94b9b63eb2315821552015fca9",
"shasum": ""
},
"require": {
@ -6499,19 +6502,15 @@
],
"support": {
"issues": "https://github.com/maennchen/ZipStream-PHP/issues",
"source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.0"
"source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.1"
},
"funding": [
{
"url": "https://github.com/maennchen",
"type": "github"
},
{
"url": "https://opencollective.com/zipstream",
"type": "open_collective"
}
],
"time": "2023-06-21T14:59:35+00:00"
"time": "2024-10-10T12:33:01+00:00"
},
{
"name": "mailgun/mailgun-php",
@ -6802,16 +6801,16 @@
},
{
"name": "mindee/mindee",
"version": "v1.11.1",
"version": "v1.12.0",
"source": {
"type": "git",
"url": "https://github.com/mindee/mindee-api-php.git",
"reference": "1ffbbdab646202f6b9547d12399841feba75c68e"
"reference": "4088e5d7e5aef72162dbea10386c64c3f071aa23"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mindee/mindee-api-php/zipball/1ffbbdab646202f6b9547d12399841feba75c68e",
"reference": "1ffbbdab646202f6b9547d12399841feba75c68e",
"url": "https://api.github.com/repos/mindee/mindee-api-php/zipball/4088e5d7e5aef72162dbea10386c64c3f071aa23",
"reference": "4088e5d7e5aef72162dbea10386c64c3f071aa23",
"shasum": ""
},
"require": {
@ -6850,9 +6849,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.11.1"
"source": "https://github.com/mindee/mindee-api-php/tree/v1.12.0"
},
"time": "2024-09-20T14:46:42+00:00"
"time": "2024-10-11T15:47:16+00:00"
},
{
"name": "mollie/mollie-api-php",
@ -7675,16 +7674,16 @@
},
{
"name": "nikic/php-parser",
"version": "v5.3.0",
"version": "v5.3.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "3abf7425cd284141dc5d8d14a9ee444de3345d1a"
"reference": "8eea230464783aa9671db8eea6f8c6ac5285794b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3abf7425cd284141dc5d8d14a9ee444de3345d1a",
"reference": "3abf7425cd284141dc5d8d14a9ee444de3345d1a",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b",
"reference": "8eea230464783aa9671db8eea6f8c6ac5285794b",
"shasum": ""
},
"require": {
@ -7727,9 +7726,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v5.3.0"
"source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1"
},
"time": "2024-09-29T13:56:26+00:00"
"time": "2024-10-08T18:51:32+00:00"
},
{
"name": "nordigen/nordigen-php",
@ -15196,16 +15195,16 @@
},
{
"name": "turbo124/beacon",
"version": "v2.0.2",
"version": "v2.0.3",
"source": {
"type": "git",
"url": "https://github.com/turbo124/beacon.git",
"reference": "95f3de3bdcbb786329cd7050f319520588920466"
"reference": "eb7ff81a49b2aa75d62459f36ad06b88181e1e00"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/turbo124/beacon/zipball/95f3de3bdcbb786329cd7050f319520588920466",
"reference": "95f3de3bdcbb786329cd7050f319520588920466",
"url": "https://api.github.com/repos/turbo124/beacon/zipball/eb7ff81a49b2aa75d62459f36ad06b88181e1e00",
"reference": "eb7ff81a49b2aa75d62459f36ad06b88181e1e00",
"shasum": ""
},
"require": {
@ -15252,9 +15251,9 @@
"turbo124"
],
"support": {
"source": "https://github.com/turbo124/beacon/tree/v2.0.2"
"source": "https://github.com/turbo124/beacon/tree/v2.0.3"
},
"time": "2024-06-27T01:23:05+00:00"
"time": "2024-10-12T06:57:20+00:00"
},
{
"name": "twig/intl-extra",
@ -17458,35 +17457,35 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "11.0.6",
"version": "11.0.7",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "ebdffc9e09585dafa71b9bffcdb0a229d4704c45"
"reference": "f7f08030e8811582cc459871d28d6f5a1a4d35ca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ebdffc9e09585dafa71b9bffcdb0a229d4704c45",
"reference": "ebdffc9e09585dafa71b9bffcdb0a229d4704c45",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f7f08030e8811582cc459871d28d6f5a1a4d35ca",
"reference": "f7f08030e8811582cc459871d28d6f5a1a4d35ca",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"ext-xmlwriter": "*",
"nikic/php-parser": "^5.1.0",
"nikic/php-parser": "^5.3.1",
"php": ">=8.2",
"phpunit/php-file-iterator": "^5.0.1",
"phpunit/php-file-iterator": "^5.1.0",
"phpunit/php-text-template": "^4.0.1",
"sebastian/code-unit-reverse-lookup": "^4.0.1",
"sebastian/complexity": "^4.0.1",
"sebastian/environment": "^7.2.0",
"sebastian/lines-of-code": "^3.0.1",
"sebastian/version": "^5.0.1",
"sebastian/version": "^5.0.2",
"theseer/tokenizer": "^1.2.3"
},
"require-dev": {
"phpunit/phpunit": "^11.0"
"phpunit/phpunit": "^11.4.1"
},
"suggest": {
"ext-pcov": "PHP extension that provides line coverage",
@ -17524,7 +17523,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.6"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.7"
},
"funding": [
{
@ -17532,7 +17531,7 @@
"type": "github"
}
],
"time": "2024-08-22T04:37:56+00:00"
"time": "2024-10-09T06:21:38+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -17781,16 +17780,16 @@
},
{
"name": "phpunit/phpunit",
"version": "11.4.0",
"version": "11.4.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "89fe0c530133c08f7fff89d3d727154e4e504925"
"reference": "7875627f15f4da7e7f0823d1f323f7295a77334e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/89fe0c530133c08f7fff89d3d727154e4e504925",
"reference": "89fe0c530133c08f7fff89d3d727154e4e504925",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7875627f15f4da7e7f0823d1f323f7295a77334e",
"reference": "7875627f15f4da7e7f0823d1f323f7295a77334e",
"shasum": ""
},
"require": {
@ -17861,7 +17860,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/11.4.0"
"source": "https://github.com/sebastianbergmann/phpunit/tree/11.4.1"
},
"funding": [
{
@ -17877,7 +17876,7 @@
"type": "tidelift"
}
],
"time": "2024-10-05T08:39:03+00:00"
"time": "2024-10-08T15:38:37+00:00"
},
{
"name": "react/cache",
@ -19280,16 +19279,16 @@
},
{
"name": "sebastian/version",
"version": "5.0.1",
"version": "5.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/version.git",
"reference": "45c9debb7d039ce9b97de2f749c2cf5832a06ac4"
"reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/version/zipball/45c9debb7d039ce9b97de2f749c2cf5832a06ac4",
"reference": "45c9debb7d039ce9b97de2f749c2cf5832a06ac4",
"url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874",
"reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874",
"shasum": ""
},
"require": {
@ -19322,7 +19321,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/version/issues",
"security": "https://github.com/sebastianbergmann/version/security/policy",
"source": "https://github.com/sebastianbergmann/version/tree/5.0.1"
"source": "https://github.com/sebastianbergmann/version/tree/5.0.2"
},
"funding": [
{
@ -19330,7 +19329,7 @@
"type": "github"
}
],
"time": "2024-07-03T05:13:08+00:00"
"time": "2024-10-09T05:16:32+00:00"
},
{
"name": "spatie/backtrace",

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.10.34'),
'app_tag' => env('APP_TAG', '5.10.34'),
'app_version' => env('APP_VERSION', '5.10.42'),
'app_tag' => env('APP_TAG', '5.10.42'),
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', false),

View File

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('vendors', function (Blueprint $table) {
if (!Schema::hasColumn('vendors', 'routing_id')) {
$table->string('routing_id')->nullable();
}
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
}
};

View File

@ -0,0 +1,56 @@
<?php
use App\Models\Currency;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Model::unguard();
$currencies = [
['id' => 124, 'name' => 'Bermudian Dollar', 'code' => 'BMD', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 125, 'name' => 'Central African CFA Franc', 'code' => 'XAF', 'symbol' => 'Fr', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 126, 'name' => 'Congolese Franc', 'code' => 'CDF', 'symbol' => 'Fr', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 127, 'name' => 'Djiboutian Franc', 'code' => 'DJF', 'symbol' => 'Fr', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 128, 'name' => 'Eritrean Nakfa', 'code' => 'ERN', 'symbol' => 'Nfk', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 129, 'name' => 'Falkland Islands Pound', 'code' => 'FKP', 'symbol' => '£', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 130, 'name' => 'Guinean Franc', 'code' => 'GNF', 'symbol' => 'Fr', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => ''],
['id' => 131, 'name' => 'Iraqi Dinar', 'code' => 'IQD', 'symbol' => 'ع.د', 'precision' => '3', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 132, 'name' => 'Lesotho Loti', 'code' => 'LSL', 'symbol' => 'L', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 133, 'name' => 'Mongolian Tugrik', 'code' => 'MNT', 'symbol' => '₮', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 134, 'name' => 'Seychellois Rupee', 'code' => 'SCR', 'symbol' => '₨', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 135, 'name' => 'Solomon Islands Dollar', 'code' => 'SBD', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 136, 'name' => 'Somali Shilling', 'code' => 'SOS', 'symbol' => 'Sh', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 137, 'name' => 'South Sudanese Pound', 'code' => 'SSP', 'symbol' => '£', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 138, 'name' => 'Sudanese Pound', 'code' => 'SDG', 'symbol' => '£', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 139, 'name' => 'Tajikistani Somoni', 'code' => 'TJS', 'symbol' => 'ЅM', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 140, 'name' => 'Turkmenistani Manat', 'code' => 'TMT', 'symbol' => 'T', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 141, 'name' => 'Uzbekistani Som', 'code' => 'UZS', 'symbol' => 'so\'m', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
];
foreach ($currencies as $currency) {
$record = Currency::where('code', $currency['code'])->first();
if (!$record) {
Currency::create($currency);
}
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
//
}
};

View File

@ -146,6 +146,25 @@ class CurrenciesSeeder extends Seeder
['id' => 121, 'name' => "Lao kip", 'code' => 'LAK', 'symbol' => '₭', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 122, 'name' => "Bhutan Ngultrum", 'code' => 'BTN', 'symbol' => 'Nu', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 123, 'name' => "Mauritanian Ouguiya", 'code' => 'MRU', 'symbol' => 'UM', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 124, 'name' => 'Bermudian Dollar', 'code' => 'BMD', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 125, 'name' => 'Central African CFA Franc', 'code' => 'XAF', 'symbol' => 'Fr', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 126, 'name' => 'Congolese Franc', 'code' => 'CDF', 'symbol' => 'Fr', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 127, 'name' => 'Djiboutian Franc', 'code' => 'DJF', 'symbol' => 'Fr', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 128, 'name' => 'Eritrean Nakfa', 'code' => 'ERN', 'symbol' => 'Nfk', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 129, 'name' => 'Falkland Islands Pound', 'code' => 'FKP', 'symbol' => '£', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 130, 'name' => 'Guinean Franc', 'code' => 'GNF', 'symbol' => 'Fr', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => ''],
['id' => 131, 'name' => 'Iraqi Dinar', 'code' => 'IQD', 'symbol' => 'ع.د', 'precision' => '3', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 132, 'name' => 'Lesotho Loti', 'code' => 'LSL', 'symbol' => 'L', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 133, 'name' => 'Mongolian Tugrik', 'code' => 'MNT', 'symbol' => '₮', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 134, 'name' => 'Seychellois Rupee', 'code' => 'SCR', 'symbol' => '₨', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 135, 'name' => 'Solomon Islands Dollar', 'code' => 'SBD', 'symbol' => '$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 136, 'name' => 'Somali Shilling', 'code' => 'SOS', 'symbol' => 'Sh', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 137, 'name' => 'South Sudanese Pound', 'code' => 'SSP', 'symbol' => '£', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 138, 'name' => 'Sudanese Pound', 'code' => 'SDG', 'symbol' => '£', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 139, 'name' => 'Tajikistani Somoni', 'code' => 'TJS', 'symbol' => 'ЅM', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 140, 'name' => 'Turkmenistani Manat', 'code' => 'TMT', 'symbol' => 'T', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 141, 'name' => 'Uzbekistani Som', 'code' => 'UZS', 'symbol' => 'so\'m', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
];
foreach ($currencies as $currency) {

View File

@ -5359,7 +5359,6 @@ $lang = array(
'updated_records' => 'Updated Records',
'vat_not_registered' => 'Seller not VAT registered',
'small_company_info' => 'No disclosure of sales tax in accordance with § 19 UStG',
'log_duration_words' => 'Log duration in words',
'peppol_onboarding' => 'Looks like it\'s your first time using PEPPOL.',
'get_started' => 'Get Started',
'configure_peppol' => 'Configure PEPPOL',
@ -5374,6 +5373,35 @@ $lang = array(
'peppol_disconnect' => 'Disconnect PEPPOL',
'peppol_disconnect_short' => 'Disconnect from PEPPOL.',
'peppol_disconnect_long' => 'PEPPOL will be disconnected from your account and...',
'log_duration_words' => 'Time log duration in words',
'log_duration' => 'Time log duration',
'merged_vendors' => 'Successfully merged vendors',
'hidden_taxes_warning' => 'Somes taxes are hidden due to current tax settings. :link',
'tax3' => 'Third Tax',
'negative_payment_warning' => 'Are you sure you want to create a negative payment? This cannot be used as a credit or payment.',
'currency_Bermudian_Dollar' => 'Bermudian Dollar',
'currency_Central_African_CFA_Franc' => 'Central African CFA Franc',
'currency_Congolese_Franc' => 'Congolese Franc',
'currency_Djiboutian_Franc' => 'Djiboutian Franc',
'currency_Eritrean_Nakfa' => 'Eritrean Nakfa',
'currency_Falkland_Islands_Pound' => 'Falklan IslandsPound',
'currency_Guinean_Franc' => 'Guinean Franc',
'currency_Iraqi_Dinar' => 'Iraqi Dinar',
'currency_Lesotho_Loti' => 'Lesotho Loti',
'currency_Mongolian_Tugrik' => 'Mongolian Tugrik',
'currency_Seychellois_Rupee' => 'Seychellois Rupee',
'currency_Solomon_Islands_Dollar' => 'Solomon Islands Dollar',
'currency_Somali_Shilling' => 'Somali Shilling',
'currency_South_Sudanese_Pound' => 'South Sudanese Pound',
'currency_Sudanese_Pound' => 'Sudanese Pound',
'currency_Tajikistani_Somoni' => 'Tajikistani Somoni',
'currency_Turkmenistani_Manat' => 'Turkmenistani Manat',
'currency_Uzbekistani_Som' => 'Uzbekistani Som',
'payment_status_changed' => 'Please note that the status of your payment has been updated. We recommend refreshing the page to view the most current version.',
'credit_status_changed' => 'Please note that the status of your credit has been updated. We recommend refreshing the page to view the most current version.',
'credit_updated' => 'Credit Updated',
'payment_updated' => 'Payment Updated',
'search_placeholder' => 'Find invoices, clients, and more',
);
return $lang;

View File

@ -5355,6 +5355,10 @@ Développe automatiquement la section des notes dans le tableau de produits pour
'quick_actions' => 'Actions rapides',
'end_all_sessions_help' => 'Déconnecte tous les utilisateurs et oblige tous les utilisateurs actifs à se réauthentifier.',
'updated_records' => 'Enregistrements mis à jour',
'vat_not_registered' => 'Vendeur non enregistré aux taxes',
'small_company_info' => 'Aucune déclaration de taxe de vente conformément à l\'article 19 UStG',
'log_duration_words' => 'Durée du journal de temps exprimée en mots',
'log_duration' => 'Durée du journal de temps'
);
return $lang;

View File

@ -2361,7 +2361,7 @@ Kom terug naar deze betaalmethode pagina zodra u de bedragen heeft ontvangen en
'currency_gold_troy_ounce' => 'Gouden Troy Ounce',
'currency_nicaraguan_córdoba' => 'Nicaraguaans Córdoba',
'currency_malagasy_ariary' => 'Malagassische ariarium',
"currency_tongan_paanga" => "Tongaanse pa'anga",
"currency_tongan_pa_anga" => "Tongaanse pa'anga",
'review_app_help' => 'We hopen dat u veel gemak heeft aan het gebruik van deze app.<br/> Als u :link zou overwegen, stellen wij dat zeer op prijs!',
'writing_a_review' => 'een recensie schrijven',
@ -5338,6 +5338,29 @@ E-mail: :email<b><br><b>',
'no_unread_notifications' => 'U bent weer helemaal bij! Er zijn geen nieuwe notificaties.',
'how_to_import_data' => 'Hoe u gegevens kunt importeren',
'download_example_file' => 'Download voorbeeldbestand',
'expense_mailbox' => 'Inkomend e-mailadres',
'expense_mailbox_help' => 'Het inkomende e-mailadres waarop declaraties ontvangen kunnen worden. Bijv. declaratie@invoiceninja.com',
'expense_mailbox_active' => 'Mailbox voor declaraties',
'expense_mailbox_active_help' => 'Maakt het mogelijk om declaraties zoals kassabonnen te verwerken voor kostenrapportages',
'inbound_mailbox_allow_company_users' => 'Bedrijfsafzenders toestaan',
'inbound_mailbox_allow_company_users_help' => 'Sta gebruikers binnen het bedrijf toe om declaraties in te sturen',
'inbound_mailbox_allow_vendors' => 'Leveranciersafzenders toestaan',
'inbound_mailbox_allow_vendors_help' => 'Sta bedrijfsafzenders toe om declaraties in te sturen',
'inbound_mailbox_allow_clients' => 'Sta klantafzenders toe',
'inbound_mailbox_allow_clients_help' => 'Sta klanten toe om declaraties in te sturen',
'inbound_mailbox_whitelist' => 'Lijst met toegestane afzenders',
'inbound_mailbox_whitelist_help' => 'Door komma\'s gescheiden lijst met e-mailadressen die declaraties mogen insturen voor verwerking',
'inbound_mailbox_blacklist' => 'Lijst met geblokkeerde afzenders',
'inbound_mailbox_blacklist_help' => 'Door komma\'s gescheiden lijst met e-mailadressen die geblokkeerd zijn en declaraties niet mogen insturen voor verwerking',
'inbound_mailbox_allow_unknown' => 'Sta alle afzenders toe',
'inbound_mailbox_allow_unknown_help' => 'Sta iedereen toe om declaraties in te sturen voor verwerking',
'quick_actions' => 'Snelle acties',
'end_all_sessions_help' => 'Logt alle gebruikers uit en vereist dat alle actieve gebruikers opnieuw inloggen.',
'updated_records' => 'Items bijgewerkt',
'vat_not_registered' => 'Verkoper is niet btw-plichtig',
'small_company_info' => 'Geen openbaarmaking van omzetbelasting in overeenstemming met § 19 UStG',
'log_duration_words' => 'Maximale lengte logboek in woorden',
'log_duration' => 'Maximale lengte logboek'
);
return $lang;

View File

@ -1065,7 +1065,7 @@ $lang = array(
'user_create_all' => 'Ustvarja stranke, račune, itd.',
'user_view_all' => 'Vidi stranke, račune, itd.',
'user_edit_all' => 'Ureja stranke, račune, itd.',
'partial_due' => 'Delno plačilo do',
'partial_due' => 'Delno plačilo',
'restore_vendor' => 'Obnovi dobavitelja',
'restored_vendor' => 'Dobavitelj uspešno obnovljen',
'restored_expense' => 'Strošek uspešno obnovljen',
@ -2365,7 +2365,7 @@ Ko imate zneske, se vrnite na to stran plačilnega sredstva in kliknite na "Comp
'currency_gold_troy_ounce' => 'Gold Troy Ounce',
'currency_nicaraguan_córdoba' => 'Nicaraguan Córdoba',
'currency_malagasy_ariary' => 'Malagasy ariary',
"currency_tongan_paanga" => "Tongan Pa'anga",
"currency_tongan_pa_anga" => "Tongan Pa'anga",
'review_app_help' => 'Upamo da uživate v uporabi aplikacije.<br/>Zelo bi cenili klik na :link!',
'writing_a_review' => 'pisanje pregleda (kritike)',
@ -2493,6 +2493,8 @@ Ko imate zneske, se vrnite na to stran plačilnega sredstva in kliknite na "Comp
'local_storage_required' => 'Napaka: Lokalna hramba ni na voljo.',
'your_password_reset_link' => 'Povezava za ponastavitev gesla',
'subdomain_taken' => 'Poddomena že v uporabi',
'expense_mailbox_taken' => 'The inbound mailbox is already in use',
'expense_mailbox_invalid' => 'The inbound mailbox does not match the required schema',
'client_login' => 'Vpis stranke',
'converted_amount' => 'Pretvorjeni znesek',
'default' => 'Privzeto',
@ -3890,7 +3892,7 @@ Ko imate zneske, se vrnite na to stran plačilnega sredstva in kliknite na "Comp
'payment_method_saving_failed' => 'Payment method can\'t be saved for future use.',
'pay_with' => 'Pay with',
'n/a' => 'N/A',
'by_clicking_next_you_accept_terms' => 'By clicking "Next step" you accept terms.',
'by_clicking_next_you_accept_terms' => 'By clicking "Next" you accept terms.',
'not_specified' => 'Not specified',
'before_proceeding_with_payment_warning' => 'Before proceeding with payment, you have to fill following fields',
'after_completing_go_back_to_previous_page' => 'After completing, go back to previous page.',
@ -5125,7 +5127,7 @@ Ko imate zneske, se vrnite na to stran plačilnega sredstva in kliknite na "Comp
'all_contacts' => 'All Contacts',
'insert_below' => 'Insert Below',
'nordigen_handler_subtitle' => 'Bank account authentication. Selecting your institution to complete the request with your account credentials.',
'nordigen_handler_error_heading_unknown' => 'An error has occured',
'nordigen_handler_error_heading_unknown' => 'An error has occurred',
'nordigen_handler_error_contents_unknown' => 'An unknown error has occurred! Reason:',
'nordigen_handler_error_heading_token_invalid' => 'Invalid Token',
'nordigen_handler_error_contents_token_invalid' => 'The provided token was invalid. Contact support for help, if this issue persists.',
@ -5239,7 +5241,7 @@ Ko imate zneske, se vrnite na to stran plačilnega sredstva in kliknite na "Comp
'local_domain_help' => 'EHLO domain (optional)',
'port_help' => 'ie. 25,587,465',
'host_help' => 'ie. smtp.gmail.com',
'always_show_required_fields' => 'Allows show required fields form',
'always_show_required_fields' => 'Always show required fields form',
'always_show_required_fields_help' => 'Displays the required fields form always at checkout',
'advanced_cards' => 'Advanced Cards',
'activity_140' => 'Statement sent to :client',
@ -5302,6 +5304,63 @@ Ko imate zneske, se vrnite na to stran plačilnega sredstva in kliknite na "Comp
'latest_requires_php_version' => 'Note: the latest version requires PHP :version',
'auto_expand_product_table_notes' => 'Automatically expand products table notes',
'auto_expand_product_table_notes_help' => 'Automatically expands the notes section within the products table to display more lines.',
'institution_number' => 'Institution Number',
'transit_number' => 'Transit Number',
'personal' => 'Personal',
'address_information' => 'Address Information',
'enter_the_information_for_the_bank_account' => 'Enter the Information for the Bank Account',
'account_holder_information' => 'Account Holder Information',
'enter_information_for_the_account_holder' => 'Enter Information for the Account Holder',
'customer_type' => 'Customer Type',
'process_date' => 'Process Date',
'forever_free' => 'Forever Free',
'comments_only' => 'Comments Only',
'payment_balance_on_file' => 'Payment Balance On File',
'ubl_email_attachment_help' => 'For more e-invoice settings please navigate :here',
'stop_task_to_add_task_entry' => 'You need to stop the task before adding a new item.',
'xml_file' => 'XML File',
'one_page_checkout' => 'One-Page Checkout',
'one_page_checkout_help' => 'Enable the new single page payment flow',
'applies_to' => 'Applies To',
'accept_purchase_order' => 'Accept Purchase Order',
'round_to_seconds' => 'Round To Seconds',
'activity_142' => 'Quote :number reminder 1 sent',
'activity_143' => 'Auto Bill succeeded for invoice :invoice',
'activity_144' => 'Auto Bill failed for invoice :invoice. :notes',
'activity_145' => 'EInvoice :invoice for :client was e-delivered. :notes',
'payment_failed' => 'Payment Failed',
'ssl_host_override' => 'SSL Host Override',
'upload_logo_short' => 'Upload Logo',
'country_Melilla' => 'Melilla',
'country_Ceuta' => 'Ceuta',
'country_Canary Islands' => 'Canary Islands',
'lang_Vietnamese' => 'Vietnamese',
'invoice_status_changed' => 'Please note that the status of your invoice has been updated. We recommend refreshing the page to view the most current version.',
'no_unread_notifications' => 'Youre all caught up! No new notifications.',
'how_to_import_data' => 'How to import data',
'download_example_file' => 'Download example file',
'expense_mailbox' => 'Inbound e-mail address',
'expense_mailbox_help' => 'The inbound email address which accepts expense documents. ie. expense@invoiceninja.com',
'expense_mailbox_active' => 'Expense Mailbox',
'expense_mailbox_active_help' => 'Enables processing of documents such as receipts for expense reporting',
'inbound_mailbox_allow_company_users' => 'Allow Company Senders',
'inbound_mailbox_allow_company_users_help' => 'Allows users within the company to send expense documents.',
'inbound_mailbox_allow_vendors' => 'Allow Vendor Senders',
'inbound_mailbox_allow_vendors_help' => 'Allows company vendors to send expense documents',
'inbound_mailbox_allow_clients' => 'Allow Client Senders',
'inbound_mailbox_allow_clients_help' => 'Allows clients to send expense documents',
'inbound_mailbox_whitelist' => 'Inbound sender allow list',
'inbound_mailbox_whitelist_help' => 'Comma separated list of emails that should be allowed to send emails for processing',
'inbound_mailbox_blacklist' => 'Inbound sender banned list',
'inbound_mailbox_blacklist_help' => 'Comma separate list of emails that are disallowed to send emails for processing',
'inbound_mailbox_allow_unknown' => 'Allow All Senders',
'inbound_mailbox_allow_unknown_help' => 'Allow anyone to send an expense email for processing',
'quick_actions' => 'Quick Actions',
'end_all_sessions_help' => 'Logs out all users and requires all active users to reauthenticate.',
'updated_records' => 'Updated Records',
'vat_not_registered' => 'Seller not VAT registered',
'small_company_info' => 'No disclosure of sales tax in accordance with § 19 UStG',
'log_duration_words' => 'Log duration in words'
);
return $lang;

View File

@ -13,7 +13,7 @@ $lang = array(
'postal_code' => 'Mã bưu chính',
'country_id' => 'Quốc gia',
'contacts' => 'Liên hệ',
'first_name' => 'Họ & tên',
'first_name' => 'Họ',
'last_name' => 'Tên',
'phone' => 'Số điện thoại',
'email' => 'Email',
@ -26,7 +26,7 @@ $lang = array(
'invoice' => 'Hóa đơn',
'client' => 'Khách hàng',
'invoice_date' => 'Ngày hóa đơn',
'due_date' => 'Hạn thanh toán',
'due_date' => 'Đã thanh toán',
'invoice_number' => 'Số hóa đơn',
'invoice_number_short' => 'Hóa đơn #',
'po_number' => 'Số PO',
@ -42,7 +42,7 @@ $lang = array(
'line_total' => 'Tổng',
'subtotal' => 'Thành tiền',
'net_subtotal' => 'Tính',
'paid_to_date' => 'Hạn thanh toán',
'paid_to_date' => 'Đã thanh toán đến ngày',
'balance_due' => 'Số tiền thanh toán',
'invoice_design_id' => 'Thiết kế',
'terms' => 'Điều khoản',
@ -135,7 +135,7 @@ $lang = array(
'status' => 'Trạng thái',
'invoice_total' => 'Tổng hóa đơn',
'frequency' => 'Chu kỳ',
'range' => 'phạm vi',
'range' => 'Phạm vi',
'start_date' => 'Ngày bắt đầu',
'end_date' => 'Ngày kết thúc',
'transaction_reference' => 'Tham chiếu giao dịch',
@ -386,7 +386,7 @@ $lang = array(
'more_designs_self_host_text' => '',
'buy' => 'Mua',
'bought_designs' => 'Đã thêm thành công các thiết kế hóa đơn bổ sung',
'sent' => 'gởi',
'sent' => 'Đã gửi',
'vat_number' => 'Số VAT',
'payment_title' => 'Nhập địa chỉ thanh toán và thông tin thẻ tín dụng của bạn',
'payment_cvv' => '* Đây là số 3-4 chữ số ở mặt sau thẻ của bạn',
@ -738,7 +738,7 @@ $lang = array(
'activity_7' => ':contact đã xem hóa đơn :invoice gửi đến :client',
'activity_8' => ':user hóa đơn lưu trữ :invoice',
'activity_9' => ':user đã xóa hóa đơn :invoice',
'activity_10' => ':user đã nhập thanh toán :payment cho :payment _số tiền trên hóa đơn :invoice cho :client',
'activity_10' => ':user đã nhập thanh toán :payment bởi :payment _số tiền trên hóa đơn :invoice cho :client',
'activity_11' => ':user cập nhật thanh toán :payment',
'activity_12' => ':user thanh toán đã lưu trữ :payment',
'activity_13' => ':user đã xóa thanh toán :payment',
@ -835,7 +835,7 @@ $lang = array(
'invalid_csv_header' => 'Tiêu đề CSV không hợp lệ',
'client_portal' => 'Cổng thông tin khách hàng',
'admin' => 'Quản trị viên',
'disabled' => 'Tàn tật',
'disabled' => 'Vô hiệu hóa',
'show_archived_users' => 'Hiển thị người dùng đã lưu trữ',
'notes' => 'Ghi chú',
'invoice_will_create' => 'hóa đơn sẽ được tạo',
@ -959,7 +959,7 @@ $lang = array(
'quote_message_button' => 'Để xem báo giá cho :amount , hãy nhấp vào nút bên dưới.',
'payment_message_button' => 'Cảm ơn bạn đã thanh toán :amount .',
'payment_type_direct_debit' => 'Ghi nợ trực tiếp',
'bank_accounts' => 'Thẻ tín dụng & ngân hàng',
'bank_accounts' => 'Thẻ tín dụng & Ngân hàng',
'add_bank_account' => 'Thêm tài khoản ngân hàng',
'setup_account' => 'Thiết lập tài khoản',
'import_expenses' => 'Chi phí nhập khẩu',
@ -2364,7 +2364,7 @@ $lang = array(
'currency_gold_troy_ounce' => 'Ounce vàng Troy',
'currency_nicaraguan_córdoba' => 'Córdoba Nicaragua',
'currency_malagasy_ariary' => 'Tiếng Malagasy',
"currency_tongan_paanga" => "Tonga Pa&#39;anga",
"currency_tongan_pa_anga" => "Tongan Pa'anga",
'review_app_help' => 'Chúng tôi hy vọng bạn thích sử dụng ứng dụng này.<br/> Nếu bạn cân nhắc :link chúng tôi sẽ rất cảm kích!',
'writing_a_review' => 'viết đánh giá',
@ -3097,7 +3097,7 @@ $lang = array(
'uploaded_logo' => 'Đã tải logo thành công',
'saved_settings' => 'Đã lưu cài đặt thành công',
'device_settings' => 'Cài đặt thiết bị',
'credit_cards_and_banks' => 'Thẻ tín dụng & ngân hàng',
'credit_cards_and_banks' => 'Thẻ tín dụng & Ngân hàng',
'price' => 'Giá',
'email_sign_up' => 'Đăng ký Email',
'google_sign_up' => 'Đăng ký Google',
@ -3738,8 +3738,8 @@ $lang = array(
'document_upload_help' => 'Cho phép khách hàng tải lên tài liệu',
'expense_total' => 'Tổng chi phí',
'enter_taxes' => 'Nhập Thuế',
'by_rate' => 'Theo Tỷ giá',
'by_amount' => 'Theo Số Lượng',
'by_rate' => 'Theo tỷ giá',
'by_amount' => 'Theo số lượng',
'enter_amount' => 'Nhập số tiền',
'before_taxes' => 'Trước thuế',
'after_taxes' => 'Sau thuế',
@ -4106,7 +4106,7 @@ $lang = array(
'auto_bill_disabled' => 'Tự động hóa đơn bị vô hiệu hóa',
'select_payment_method' => 'Chọn phương thức thanh toán:',
'login_without_password' => 'Đăng nhập không cần mật khẩu',
'email_sent' => 'Email khi một hóa đơn được <b>gởi</b>',
'email_sent' => 'Email khi một hóa đơn được <b>đã gửi</b>',
'one_time_purchases' => 'Mua một lần',
'recurring_purchases' => 'Mua hàng định kỳ',
'you_might_be_interested_in_following' => 'Bạn có thể quan tâm đến những điều sau đây',
@ -4266,7 +4266,7 @@ $lang = array(
'uninvoiced' => 'Chưa xuất hóa đơn',
'subdomain_guide' => 'Tên miền phụ được sử dụng trong cổng thông tin khách hàng để cá nhân hóa các liên kết phù hợp với thương hiệu của bạn. Ví dụ: https://your-brand.invoicing.co',
'send_time' => 'Gửi thời gian',
'import_settings' => 'Nhập Cài Đặt',
'import_settings' => 'Nhập cài đặt',
'json_file_missing' => 'Vui lòng cung cấp tệp JSON',
'json_option_missing' => 'Vui lòng chọn để nhập cài đặt và/hoặc dữ liệu',
'json' => 'JSON',
@ -4975,7 +4975,7 @@ $lang = array(
'e_invoice' => 'Hóa đơn điện tử',
'light_dark_mode' => 'Chế độ sáng/tối',
'activities' => 'Các hoạt động',
'recent_transactions' => "Sau đây là các giao dịch gần đây nhất của công ty bạn:",
'recent_transactions' => "Các giao dịch gần đây nhất của công ty bạn:",
'country_Palestine' => "Palestine",
'country_Taiwan' => 'Đài Loan',
'duties' => 'Nhiệm vụ',
@ -5031,7 +5031,7 @@ $lang = array(
'county' => 'Quận',
'tax_details' => 'Chi tiết thuế',
'activity_10_online' => ':contact đã thanh toán :payment cho hóa đơn :invoice cho :client',
'activity_10_manual' => ':user đã nhập thanh toán :payment cho hóa đơn :invoice cho :client',
'activity_10_manual' => ':user đã nhập thanh toán :payment bởi hóa đơn :invoice cho :client',
'default_payment_type' => 'Loại thanh toán mặc định',
'number_precision' => 'Độ chính xác của số',
'number_precision_help' => 'Kiểm soát số lượng số thập phân được hỗ trợ trong giao diện',
@ -5355,7 +5355,12 @@ $lang = array(
'inbound_mailbox_allow_unknown' => 'Cho phép tất cả người gửi',
'inbound_mailbox_allow_unknown_help' => 'Cho phép bất cứ ai đến gửi email Chi phí để xử lý',
'quick_actions' => 'Hành động nhanh',
'end_all_sessions_help' => 'Đăng xuất tất cả người dùng và yêu cầu tất cả người dùng đang hoạt động đến xác thực lại.'
'end_all_sessions_help' => 'Đăng xuất tất cả người dùng và yêu cầu tất cả người dùng đang hoạt động đến xác thực lại.',
'updated_records' => 'Hồ sơ đã cập nhật',
'vat_not_registered' => 'Người bán không đăng ký VAT',
'small_company_info' => 'Không tiết lộ thuế bán hàng theo § 19 UStG',
'log_duration_words' => 'Thời gian ghi nhật ký bằng từ',
'log_duration' => 'Thời gian ghi nhật ký'
);
return $lang;

File diff suppressed because one or more lines are too long

View File

@ -1,9 +0,0 @@
import{i,w as d}from"./wait-8f4ae121.js";/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/class s{constructor(){this.appId=document.querySelector("meta[name=square-appId]").content,this.locationId=document.querySelector("meta[name=square-locationId]").content,this.isLoaded=!1}async init(){this.payments=Square.payments(this.appId,this.locationId),this.card=await this.payments.card(),await this.card.attach("#card-container"),this.isLoaded=!0;let e=document.querySelector(".sq-card-iframe-container");e&&e.setAttribute("style","150px !important"),document.querySelector(".toggle-payment-with-token")&&document.getElementById("card-container").classList.add("hidden")}async completePaymentWithoutToken(e){document.getElementById("errors").hidden=!0,e.target.parentElement.disabled=!0;let t=await this.card.tokenize(),o;try{const n={amount:document.querySelector("meta[name=amount]").content,billingContact:JSON.parse(document.querySelector("meta[name=square_contact]").content),currencyCode:document.querySelector("meta[name=currencyCode]").content,intent:"CHARGE"};o=(await this.payments.verifyBuyer(t.token,n)).token}catch{e.target.parentElement.disabled=!0}if(document.querySelector('input[name="verificationToken"]').value=o,t.status==="OK"){document.getElementById("sourceId").value=t.token;let n=document.querySelector('input[name="token-billing-checkbox"]:checked');return n&&(document.querySelector('input[name="store_card"]').value=n.value),document.getElementById("server_response").submit()}document.getElementById("errors").textContent=t.errors[0].message,document.getElementById("errors").hidden=!1,e.target.parentElement.disabled=!1}async completePaymentUsingToken(e){return e.target.parentElement.disabled=!0,document.getElementById("server_response").submit()}async verifyBuyer(e){const t={amount:document.querySelector("meta[name=amount]").content,billingContact:document.querySelector("meta[name=square_contact]").content,currencyCode:document.querySelector("meta[name=currencyCode]").content,intent:"CHARGE"};return(await this.payments.verifyBuyer(e,t)).token}async handle(){document.getElementById("payment-list").classList.add("hidden"),await this.init().then(()=>{var e,t,o,n;(e=document.getElementById("authorize-card"))==null||e.addEventListener("click",a=>this.completePaymentWithoutToken(a)),(t=document.getElementById("pay-now"))==null||t.addEventListener("click",a=>document.querySelector("input[name=token]").value?this.completePaymentUsingToken(a):this.completePaymentWithoutToken(a)),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(a=>a.addEventListener("click",async r=>{document.getElementById("card-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=r.target.dataset.token})),(o=document.getElementById("toggle-payment-with-credit-card"))==null||o.addEventListener("click",async a=>{document.getElementById("card-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value=""}),document.getElementById("loader").classList.add("hidden"),document.getElementById("payment-list").classList.remove("hidden"),(n=document.getElementById("toggle-payment-with-credit-card"))==null||n.click()})}}function c(){new s().handle()}i()?c():d("#square-credit-card-payment").then(()=>c());

View File

@ -0,0 +1,9 @@
import{i as c,w as d}from"./wait-8f4ae121.js";/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/class s{constructor(){this.appId=document.querySelector("meta[name=square-appId]").content,this.locationId=document.querySelector("meta[name=square-locationId]").content,this.isLoaded=!1}async init(){this.payments=Square.payments(this.appId,this.locationId),this.card=await this.payments.card(),await this.card.attach("#card-container"),this.isLoaded=!0;let t=document.querySelector(".sq-card-iframe-container");t&&t.setAttribute("style","150px !important"),document.querySelector(".toggle-payment-with-token")&&document.getElementById("card-container").classList.add("hidden")}async completePaymentWithoutToken(t){document.getElementById("errors").hidden=!0,t.target.parentElement.disabled=!0;let n=document.getElementById("pay-now");this.payNowButton=n,this.payNowButton.disabled=!0,this.payNowButton.querySelector("svg").classList.remove("hidden"),this.payNowButton.querySelector("span").classList.add("hidden");let a=await this.card.tokenize(),o;try{const e={amount:document.querySelector("meta[name=amount]").content,billingContact:JSON.parse(document.querySelector("meta[name=square_contact]").content),currencyCode:document.querySelector("meta[name=currencyCode]").content,intent:"CHARGE"};o=(await this.payments.verifyBuyer(a.token,e)).token}catch{t.target.parentElement.disabled=!0}if(document.querySelector('input[name="verificationToken"]').value=o,a.status==="OK"){document.getElementById("sourceId").value=a.token;let e=document.querySelector('input[name="token-billing-checkbox"]:checked');return e&&(document.querySelector('input[name="store_card"]').value=e.value),document.getElementById("server_response").submit()}document.getElementById("errors").textContent=a.errors[0].message,document.getElementById("errors").hidden=!1,t.target.parentElement.disabled=!1,this.payNowButton.disabled=!1,this.payNowButton.querySelector("svg").classList.add("hidden"),this.payNowButton.querySelector("span").classList.remove("hidden")}async completePaymentUsingToken(t){t.target.parentElement.disabled=!0;let n=document.getElementById("pay-now");return this.payNowButton=n,this.payNowButton.disabled=!0,this.payNowButton.querySelector("svg").classList.remove("hidden"),this.payNowButton.querySelector("span").classList.add("hidden"),document.getElementById("server_response").submit()}async verifyBuyer(t){const n={amount:document.querySelector("meta[name=amount]").content,billingContact:document.querySelector("meta[name=square_contact]").content,currencyCode:document.querySelector("meta[name=currencyCode]").content,intent:"CHARGE"};return(await this.payments.verifyBuyer(t,n)).token}async handle(){document.getElementById("payment-list").classList.add("hidden"),await this.init().then(()=>{var t,n,a,o;(t=document.getElementById("authorize-card"))==null||t.addEventListener("click",e=>this.completePaymentWithoutToken(e)),(n=document.getElementById("pay-now"))==null||n.addEventListener("click",e=>document.querySelector("input[name=token]").value?this.completePaymentUsingToken(e):this.completePaymentWithoutToken(e)),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(e=>e.addEventListener("click",async r=>{document.getElementById("card-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=r.target.dataset.token})),(a=document.getElementById("toggle-payment-with-credit-card"))==null||a.addEventListener("click",async e=>{document.getElementById("card-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value=""}),Array.from(document.getElementsByClassName("loader")).forEach(e=>{e.classList.add("hidden")}),document.getElementById("payment-list").classList.remove("hidden"),(o=document.getElementById("toggle-payment-with-credit-card"))==null||o.click()})}}function i(){new s().handle()}c()?i():d("#square-credit-card-payment").then(()=>i());

Some files were not shown because too many files have changed in this diff Show More