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

This commit is contained in:
paulwer 2024-05-19 06:46:54 +02:00
commit 9c9c4a998c
275 changed files with 488225 additions and 436031 deletions

View File

@ -28,15 +28,6 @@ REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="user@example.com"
MAIL_FROM_NAME="Self Hosted User"
POSTMARK_API_TOKEN=
REQUIRE_HTTPS=false

View File

@ -64,7 +64,7 @@ jobs:
- name: Build project
run: |
shopt -s dotglob
tar --exclude='public/storage' --exclude='.htaccess' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/react-invoiceninja.tar *
tar --exclude='public/storage' --exclude='./htaccess' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/react-invoiceninja.tar *
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')

View File

@ -74,7 +74,7 @@ jobs:
run: |
zip -r /home/runner/work/invoiceninja/invoiceninja.zip .* -x "../*"
shopt -s dotglob
tar --exclude='public/storage' --exclude='.htaccess' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar *
tar --exclude='public/storage' --exclude='./htaccess' --exclude='invoiceninja.zip' -zcvf /home/runner/work/invoiceninja/invoiceninja.tar *
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')

View File

@ -1 +1 @@
5.8.50
5.8.57

View File

@ -208,7 +208,7 @@ class CheckData extends Command
->cursor()
->each(function ($client) {
if ($client->recurring_invoices()->where('is_deleted', 0)->where('deleted_at', null)->count() > 1) {
$this->logMessage("Duplicate Recurring Invoice => {$client->custom_value1}");
$this->logMessage("Duplicate Recurring Invoice => {$client->custom_value1} || {$client->id}}");
}
});
}

View File

@ -20,7 +20,7 @@ class ReactBuilder extends Command
*
* @var string
*/
protected $signature = 'ninja:react';
protected $signature = 'ninja:react {--type=}';
/**
* The console command description.
@ -46,31 +46,39 @@ class ReactBuilder extends Command
*/
public function handle()
{
// $includes = '';
if($this->option('type') == 'local') {
// $directoryIterator = false;
// try {
// $directoryIterator = new \RecursiveDirectoryIterator(public_path('react/v'.config('ninja.app_version').'/'), \RecursiveDirectoryIterator::SKIP_DOTS);
// } catch (\Exception $e) {
// $this->error('React files not found');
// return;
// }
// foreach (new \RecursiveIteratorIterator($directoryIterator) as $file) {
// if ($file->getExtension() == 'js') {
// if (str_contains($file->getFileName(), 'index-')) {
// $includes .= '<script type="module" crossorigin src="/react/v'.config('ninja.app_version').'/'.$file->getFileName().'"></script>'."\n";
// } else {
// $includes .= '<link rel="modulepreload" href="/react/v'.config('ninja.app_version').'/'.$file->getFileName().'">'."\n";
// }
// }
$includes = '';
$directoryIterator = false;
try {
$directoryIterator = new \RecursiveDirectoryIterator(public_path('react/v'.config('ninja.app_version').'/'), \RecursiveDirectoryIterator::SKIP_DOTS);
} catch (\Exception $e) {
$this->error('React files not found');
return;
}
foreach (new \RecursiveIteratorIterator($directoryIterator) as $file) {
if ($file->getExtension() == 'js') {
if (str_contains($file->getFileName(), 'index-')) {
$includes .= '<script type="module" crossorigin src="/react/v'.config('ninja.app_version').'/'.$file->getFileName().'"></script>'."\n";
} else {
$includes .= '<link rel="modulepreload" href="/react/v'.config('ninja.app_version').'/'.$file->getFileName().'">'."\n";
}
}
if (str_contains($file->getFileName(), '.css')) {
$includes .= '<link rel="stylesheet" href="/react/v'.config('ninja.app_version').'/'.$file->getFileName().'">'."\n";
}
}
file_put_contents(resource_path('views/react/head.blade.php'), $includes);
}
// if (str_contains($file->getFileName(), '.css')) {
// $includes .= '<link rel="stylesheet" href="/react/v'.config('ninja.app_version').'/'.$file->getFileName().'">'."\n";
// }
// }
// file_put_contents(resource_path('views/react/head.blade.php'), $includes);
}
}

View File

@ -30,7 +30,7 @@ class RevenueTrack extends GenericMixedMetric
* The name of the counter.
* @var string
*/
public $name = 'app.revenue';
public $name = 'app.cac';
/**
* The datetime of the counter measurement.
@ -59,14 +59,14 @@ class RevenueTrack extends GenericMixedMetric
*
* @var string
*/
public $string_metric7 = 'product';
public $string_metric7 = 'plan';
/**
* Gateway Reference
*
* @var string
*/
public $string_metric8 = 'gateway_reference';
public $string_metric8 = 'plan_term';
public $string_metric9 = 'entity_reference';

View File

@ -503,7 +503,13 @@ class CompanySettings extends BaseSettings
public $enable_rappen_rounding = false;
public bool $task_round_up = true;
public int $task_round_to_nearest = 1;
public static $casts = [
'task_round_up' => 'bool',
'task_round_to_nearest' => 'int',
'e_quote_type' => 'string',
'enable_rappen_rounding' => 'bool',
'use_unapplied_payment' => 'string',

View File

@ -1,37 +0,0 @@
<?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\DataMapper\EDoc;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Optional;
use App\DataMapper\EDoc\FatturaPA\DatiContratto;
use App\DataMapper\EDoc\FatturaPA\DatiRicezione;
use App\DataMapper\EDoc\FatturaPA\DatiOrdineAcquisto;
use App\DataMapper\EDoc\FatturaPA\DatiAnagraficiVettore;
class FatturaPA extends Data
{
public DatiRicezione|Optional $DatiRicezione;
public DatiContratto|Optional $DatiContratto;
public DatiOrdineAcquisto|Optional $DatiOrdineAcquisto;
public DatiAnagraficiVettore|Optional $DatiAnagraficiVettore;
public function __construct(
public string $RegimeFiscale = 'RF01',
public string $TipoDocumento = 'TD01',
public string $ModalitaPagamento = 'MP01',
public string $CondizioniPagamento = 'TP02',
) {
}
}

View File

@ -1,28 +0,0 @@
<?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\DataMapper\EDoc\FatturaPA;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Attributes\WithTransformer;
use Spatie\LaravelData\Transformers\DateTimeInterfaceTransformer;
class DatiAnagraficiVettore extends Data
{
public function __construct(
public string $IdFiscaleIVA = '',
public string $CodiceFiscale = '',
public string $Anagrafica = '',
#[WithTransformer(DateTimeInterfaceTransformer::class, format: 'Y-m-d\TH:i:s.uP')]
public \DateTime $DataOraConsegna = new \DateTime(),
){}
}

View File

@ -1,28 +0,0 @@
<?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\DataMapper\EDoc\FatturaPA;
use Spatie\LaravelData\Data;
class DatiContratto extends Data
{
public function __construct(
public string $RiferimentoNumeroLinea = '',
public string $IdDocumento = '',
public string $Data = '',
public string $NumItem = '',
public string $CodiceCommessaConvenzione = '',
public string $CodiceCUP = '',
public string $CodiceCIG = '',
) {
}
}

View File

@ -1,28 +0,0 @@
<?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\DataMapper\EDoc\FatturaPA;
use Spatie\LaravelData\Data;
class DatiOrdineAcquisto extends Data
{
public function __construct(
public string $RiferimentoNumeroLinea = '',
public string $IdDocumento = '',
public string $Data = '',
public string $NumItem = '',
public string $CodiceCommessaConvenzione = '',
public string $CodiceCUP = '',
public string $CodiceCIG = '',
) {
}
}

View File

@ -1,28 +0,0 @@
<?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\DataMapper\EDoc\FatturaPA;
use Spatie\LaravelData\Data;
class DatiRicezione extends Data
{
public function __construct(
public string $RiferimentoNumeroLinea = '',
public string $IdDocumento = '',
public string $Data = '',
public string $NumItem = '',
public string $CodiceCommessaConvenzione = '',
public string $CodiceCUP = '',
public string $CodiceCIG = '',
) {
}
}

View File

@ -13,17 +13,17 @@ namespace App\DataMapper;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Optional;
use App\DataMapper\EDoc\FatturaPA;
use Invoiceninja\Einvoice\Models\FatturaPA\FatturaElettronica;
class EDocSettings extends Data
{
public FatturaPA|Optional $FatturaPA;
public FatturaElettronica|Optional $FatturaElettronica;
public function __construct() {}
public function createFatturaPA(): FatturaPA
public function createFatturaPA(): FatturaElettronica
{
return $this->FatturaPA ??= new FatturaPA();
return $this->FatturaElettronica ??= new FatturaElettronica;
}
}

View File

@ -11,11 +11,12 @@
namespace App\DataMapper\Tax;
use App\DataMapper\Tax\ZipTax\Response;
use App\DataProviders\USStates;
use App\Models\Quote;
use App\Models\Client;
use App\Models\Invoice;
use App\Models\Product;
use App\DataProviders\USStates;
use App\DataMapper\Tax\ZipTax\Response;
class BaseRule implements RuleInterface
{
@ -210,7 +211,7 @@ class BaseRule implements RuleInterface
}
/** Applies the tax data to the invoice */
if($this->invoice instanceof Invoice && $tax_data) {
if(($this->invoice instanceof Invoice || $this->invoice instanceof Quote) && $tax_data) {
$this->invoice->tax_data = $tax_data;

View File

@ -1,290 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataProviders;
class EDocRules
{
// [
// "key" => "",
// "label" => "",
// "type" => "dropdown/date/string/text",
// "resource" => "resource.json",
// "required" => true,
// ]
public function rules()
{
return [
'FatturaPA' => $this->FatturaPADefaults(),
];
}
private function FatturaPADefaults()
{
return [
[
"key" => "RegimeFiscale",
"label" => "Regime Fiscale",
"type" => "dropdown",
"resource" => "RegimeFiscale.json",
"required" => true,
],
[
"key" => "TipoDocumento",
"label" => "Tipo Documento",
"type" => "dropdown",
"resource" => "TipoDocumento.json",
"required" => true,
],
[
"key" => "ModalitaPagamento",
"label" => "Modalita Pagamento",
"type" => "dropdown",
"resource" => "ModalitaPagamento.json",
"required" => true,
],
[
"key" => "CondizioniPagamento",
"label" => "Condizioni Pagamento",
"type" => "dropdown",
"resource" => "CondizioniPagamento.json",
"required" => true,
],
[
"key" => "DatiRicezione",
"label" => "Dati Ricezione",
"type" => "dropdown",
"resource" => "CondizioniPagamento",
"required" => false,
"children" => [],
],
[
"key" => "DatiContratto",
"label" => "Dati Contratto",
"type" => "object",
"resource" => "DatiContratto",
"required" => false,
"children" => [
[
"key"=> "RiferimentoNumeroLinea",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
[
"key"=> "IdDocumento",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
[
"key"=> "Data",
"validation" => [
"string","date","required",
"type" => "date",
"resource" => "",
"required" => true,
],
],
[
"key"=> "NumItem",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
[
"key"=> "CodiceCommessaConvenzione",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
[
"key"=> "CodiceCUP",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
[
"key"=> "CodiceCIG",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
],
],
[
"key" => "DatiOrdineAcquisto",
"label" => "Dati Ordine Acquisto",
"type" => "object",
"resource" => "DatiOrdineAcquisto",
"required" => false,
"children" => [
[
"key"=> "RiferimentoNumeroLinea",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
[
"key"=> "IdDocumento",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
[
"key"=> "Data",
"validation" => [
"string","date","required",
"type" => "date",
"resource" => "",
"required" => true,
],
],
[
"key"=> "NumItem",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
[
"key"=> "CodiceCommessaConvenzione",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
[
"key"=> "CodiceCUP",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
[
"key"=> "CodiceCIG",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
],
],
[
"key" => "DatiAnagraficiVettore",
"label" => "Dati Anagrafici Vettore",
"type" => "object",
"resource" => "DatiAnagraficiVettore",
"required" => false,
"children" => [
[
"key"=> "RiferimentoNumeroLinea",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
[
"key"=> "IdDocumento",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
[
"key"=> "Data",
"validation" => [
"string","date","required",
"type" => "date",
"resource" => "",
"required" => true,
],
],
[
"key"=> "NumItem",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
[
"key"=> "CodiceCommessaConvenzione",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
[
"key"=> "CodiceCUP",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
[
"key"=> "CodiceCIG",
"validation" => [
"string","min:1","max:10","required",
"type" => "string",
"resource" => "",
"required" => true,
],
],
],
],
];
}
}

View File

@ -0,0 +1,42 @@
<?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\Events\Statement;
use App\Models\Client;
use App\Models\Company;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
/**
* Class StatementWasEmailed.
*/
class StatementWasEmailed
{
use Dispatchable;
use InteractsWithSockets;
use SerializesModels;
public function __construct(public Client $client, public Company $company, public string $end_date, public array $event_vars)
{
}
// /**
// * Get the channels the event should broadcast on.
// *
// * @return Channel|array
// */
public function broadcastOn()
{
return [];
}
}

View File

@ -67,7 +67,6 @@ class Handler extends ExceptionHandler
];
protected $hostedDontReport = [
PDOException::class,
MaxAttemptsExceededException::class,
CommandNotFoundException::class,
ValidationException::class,

View File

@ -122,6 +122,7 @@ class ActivityExport extends BaseExport
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
//insert the header
$this->csv->insertOne($this->buildHeader());

View File

@ -448,6 +448,7 @@ class BaseExport
'custom_value4' => 'task.custom_value4',
'status' => 'task.status_id',
'project' => 'task.project_id',
'billable' => 'task.billable',
];
protected array $forced_client_fields = [
@ -879,10 +880,7 @@ class BaseExport
$transformed_clients = $this->transformKeys($clients);
nlog($transformed_clients);
if(count($transformed_clients) > 0) {
nlog("yus");
$query->whereIn('client_id', $transformed_clients);
}
@ -1283,13 +1281,13 @@ class BaseExport
$this->end_date = now()->startOfMonth()->subMonth()->endOfMonth()->format('Y-m-d');
return $query->whereBetween($this->date_key, [now()->startOfMonth()->subMonth(), now()->startOfMonth()->subMonth()->endOfMonth()])->orderBy($this->date_key, 'ASC');
case 'this_quarter':
$this->start_date = (new \Carbon\Carbon('-3 months'))->firstOfQuarter()->format('Y-m-d');
$this->end_date = (new \Carbon\Carbon('-3 months'))->lastOfQuarter()->format('Y-m-d');
return $query->whereBetween($this->date_key, [(new \Carbon\Carbon('-3 months'))->firstOfQuarter(), (new \Carbon\Carbon('-3 months'))->lastOfQuarter()])->orderBy($this->date_key, 'ASC');
$this->start_date = (new \Carbon\Carbon('0 months'))->startOfQuarter()->format('Y-m-d');
$this->end_date = (new \Carbon\Carbon('0 months'))->endOfQuarter()->format('Y-m-d');
return $query->whereBetween($this->date_key, [(new \Carbon\Carbon('0 months'))->startOfQuarter(), (new \Carbon\Carbon('0 months'))->endOfQuarter()])->orderBy($this->date_key, 'ASC');
case 'last_quarter':
$this->start_date = (new \Carbon\Carbon('-6 months'))->firstOfQuarter()->format('Y-m-d');
$this->end_date = (new \Carbon\Carbon('-6 months'))->lastOfQuarter()->format('Y-m-d');
return $query->whereBetween($this->date_key, [(new \Carbon\Carbon('-6 months'))->firstOfQuarter(), (new \Carbon\Carbon('-6 months'))->lastOfQuarter()])->orderBy($this->date_key, 'ASC');
$this->start_date = (new \Carbon\Carbon('-3 months'))->startOfQuarter()->format('Y-m-d');
$this->end_date = (new \Carbon\Carbon('-3 months'))->endOfQuarter()->format('Y-m-d');
return $query->whereBetween($this->date_key, [(new \Carbon\Carbon('-3 months'))->startOfQuarter(), (new \Carbon\Carbon('-3 months'))->endOfQuarter()])->orderBy($this->date_key, 'ASC');
case 'last365_days':
$this->start_date = now()->startOfDay()->subDays(365)->format('Y-m-d');
$this->end_date = now()->startOfDay()->format('Y-m-d');

View File

@ -41,7 +41,6 @@ class ClientExport extends BaseExport
'balance' => 'client.balance',
'city' => 'client.city',
'country' => 'client.country_id',
'credit_balance' => 'client.credit_balance',
'custom_value1' => 'client.custom_value1',
'custom_value2' => 'client.custom_value2',
'custom_value3' => 'client.custom_value3',
@ -145,6 +144,7 @@ class ClientExport extends BaseExport
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
//insert the header
$this->csv->insertOne($this->buildHeader());
@ -179,14 +179,10 @@ class ClientExport extends BaseExport
} elseif (is_array($parts) && $parts[0] == 'contact' && array_key_exists($parts[1], $transformed_contact)) {
$entity[$key] = $transformed_contact[$parts[1]];
} else {
// nlog($key);
$entity[$key] = $this->decorator->transform($key, $client);
// $entity[$key] = '';
}
}
// return $entity;
return $this->decorateAdvancedFields($client, $entity);
}
@ -229,6 +225,18 @@ class ClientExport extends BaseExport
$entity['client.classification'] = ctrans("texts.{$client->classification}") ?? '';
}
if (in_array('client.industry_id', $this->input['report_keys']) && isset($client->industry_id)) {
$entity['client.industry_id'] = ctrans("texts.industry_{$client->industry->name}") ?? '';
}
if (in_array('client.country_id', $this->input['report_keys']) && isset($client->country_id)) {
$entity['client.country_id'] = $client->country ? $client->country->full_name : '';
}
if (in_array('client.shipping_country_id', $this->input['report_keys']) && isset($client->shipping_country_id)) {
$entity['client.shipping_country_id'] = $client->shipping_country ? $client->shipping_country->full_name : '';
}
return $entity;
}

View File

@ -58,7 +58,10 @@ class ContactExport extends BaseExport
}
$query = ClientContact::query()
->where('company_id', $this->company->id);
->where('company_id', $this->company->id)
->whereHas('client', function ($q){
$q->where('is_deleted', false);
});
$query = $this->addDateRange($query);
@ -73,6 +76,7 @@ class ContactExport extends BaseExport
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
//insert the header
$this->csv->insertOne($this->buildHeader());

View File

@ -102,6 +102,9 @@ class CreditExport extends BaseExport
$query = Credit::query()
->withTrashed()
->with('client')
->whereHas('client', function ($q){
$q->where('is_deleted', false);
})
->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted'] ?? false);
@ -129,6 +132,7 @@ class CreditExport extends BaseExport
$query = $this->init();
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
//insert the header
$this->csv->insertOne($this->buildHeader());

View File

@ -92,6 +92,7 @@ class DocumentExport extends BaseExport
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
//insert the header
$this->csv->insertOne($this->buildHeader());

View File

@ -121,6 +121,7 @@ class ExpenseExport extends BaseExport
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
//insert the header
$this->csv->insertOne($this->buildHeader());

View File

@ -57,6 +57,9 @@ class InvoiceExport extends BaseExport
$query = Invoice::query()
->withTrashed()
->with('client')
->whereHas('client', function ($q){
$q->where('is_deleted', false);
})
->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted'] ?? false);
@ -105,6 +108,7 @@ class InvoiceExport extends BaseExport
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
//insert the header
$this->csv->insertOne($this->buildHeader());
@ -141,18 +145,6 @@ class InvoiceExport extends BaseExport
private function decorateAdvancedFields(Invoice $invoice, array $entity): array
{
// if (in_array('invoice.country_id', $this->input['report_keys'])) {
// $entity['invoice.country_id'] = $invoice->client->country ? ctrans("texts.country_{$invoice->client->country->name}") : '';
// }
// if (in_array('invoice.currency_id', $this->input['report_keys'])) {
// $entity['invoice.currency_id'] = $invoice->client->currency() ? $invoice->client->currency()->code : $invoice->company->currency()->code;
// }
// if (in_array('invoice.client_id', $this->input['report_keys'])) {
// $entity['invoice.client_id'] = $invoice->client->present()->name();
// }
// if (in_array('invoice.status', $this->input['report_keys'])) {
// $entity['invoice.status'] = $invoice->stringStatus($invoice->status_id);
// }

View File

@ -70,6 +70,9 @@ class InvoiceItemExport extends BaseExport
$query = Invoice::query()
->withTrashed()
->with('client')
->whereHas('client', function ($q){
$q->where('is_deleted', false);
})
->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted'] ?? false);
@ -128,6 +131,7 @@ class InvoiceItemExport extends BaseExport
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
//insert the header
$this->csv->insertOne($this->buildHeader());

View File

@ -56,6 +56,9 @@ class PaymentExport extends BaseExport
$query = Payment::query()
->withTrashed()
->whereHas('client', function ($q){
$q->where('is_deleted', false);
})
->where('company_id', $this->company->id)
->where('is_deleted', 0);
@ -102,6 +105,7 @@ class PaymentExport extends BaseExport
$query = $this->init();
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
//insert the header
$this->csv->insertOne($this->buildHeader());

View File

@ -93,6 +93,7 @@ class ProductExport extends BaseExport
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
//insert the header
$this->csv->insertOne($this->buildHeader());

View File

@ -112,6 +112,7 @@ class ProductSalesExport extends BaseExport
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->entity_keys);
@ -120,6 +121,9 @@ class ProductSalesExport extends BaseExport
//insert the header
$query = Invoice::query()
->withTrashed()
->whereHas('client', function ($q){
$q->where('is_deleted', false);
})
->where('company_id', $this->company->id)
->where('is_deleted', 0)
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL, Invoice::STATUS_PAID]);
@ -142,8 +146,14 @@ class ProductSalesExport extends BaseExport
->each(function ($invoice) use($product_keys) {
foreach ($invoice->line_items as $item) {
if($product_keys && in_array($item->product_key, $product_keys))
if($product_keys)
{
if(in_array($item->product_key, $product_keys))
$this->csv->insertOne($this->buildRow($invoice, $item));
}
else {
$this->csv->insertOne($this->buildRow($invoice, $item));
}
}
});

View File

@ -58,6 +58,9 @@ class PurchaseOrderExport extends BaseExport
$query = PurchaseOrder::query()
->withTrashed()
->with('vendor')
->whereHas('vendor', function ($q){
$q->where('is_deleted', false);
})
->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted'] ?? false);
@ -105,6 +108,7 @@ class PurchaseOrderExport extends BaseExport
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
//insert the header
$this->csv->insertOne($this->buildHeader());

View File

@ -62,6 +62,9 @@ class PurchaseOrderItemExport extends BaseExport
$query = PurchaseOrder::query()
->withTrashed()
->whereHas('vendor', function ($q){
$q->where('is_deleted', false);
})
->with('vendor')->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted'] ?? false);
@ -112,6 +115,7 @@ class PurchaseOrderItemExport extends BaseExport
{
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
$query = $this->init();

View File

@ -64,6 +64,9 @@ class QuoteExport extends BaseExport
$query = Quote::query()
->withTrashed()
->with('client')
->whereHas('client', function ($q){
$q->where('is_deleted', false);
})
->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted'] ?? false);
@ -110,6 +113,7 @@ class QuoteExport extends BaseExport
{
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
$query = $this->init();

View File

@ -65,6 +65,9 @@ class QuoteItemExport extends BaseExport
$query = Quote::query()
->withTrashed()
->whereHas('client', function ($q){
$q->where('is_deleted', false);
})
->with('client')->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted'] ?? false);
@ -118,6 +121,7 @@ class QuoteItemExport extends BaseExport
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
$query = $this->init();

View File

@ -56,6 +56,9 @@ class RecurringInvoiceExport extends BaseExport
$query = RecurringInvoice::query()
->withTrashed()
->with('client')
->whereHas('client', function ($q){
$q->where('is_deleted', false);
})
->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted'] ?? false);
@ -80,6 +83,7 @@ class RecurringInvoiceExport extends BaseExport
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
//insert the header
$this->csv->insertOne($this->buildHeader());

View File

@ -94,6 +94,7 @@ class TaskExport extends BaseExport
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
//insert the header
$this->csv->insertOne($this->buildHeader());

View File

@ -54,6 +54,7 @@ class VendorExport extends BaseExport
//load the CSV document from a string
$this->csv = Writer::createFromString();
\League\Csv\CharsetConverter::addTo($this->csv, 'UTF-8', 'UTF-8');
if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->vendor_report_keys);

View File

@ -23,8 +23,13 @@ class ContactDecorator implements DecoratorInterface
$contact = $entity;
} elseif($entity->contacts) {
$contact = $entity->contacts()->first();
} elseif($entity->client) {
$contact = $entity->client->primary_contact->first() ?? $entity->client->contacts()->whereNotNull('email')->first();
} elseif($entity->vendor) {
$contact = $entity->vendor->primary_contact->first() ?? $entity->vendor->contacts()->whereNotNull('email')->first();
}
if($contact && method_exists($this, $key)) {
return $this->{$key}($contact);
} elseif($contact && ($contact->{$key} ?? false)) {

View File

@ -23,6 +23,7 @@ class CompanyGatewayFactory
$company_gateway->require_billing_address = false;
$company_gateway->require_shipping_address = false;
$company_gateway->config = encrypt(json_encode(new \stdClass()));
$company_gateway->always_show_required_fields = true;
return $company_gateway;
}

View File

@ -160,6 +160,9 @@ class ClientFilters extends QueryFilters
return $this->builder;
}
if($sort_col[0] == 'documents')
return $this->builder;
if ($sort_col[0] == 'display_name') {
$sort_col[0] = 'name';
}

View File

@ -107,6 +107,12 @@ class ExpenseFilters extends QueryFilters
$query->whereNull('payment_date');
});
}
if(in_array('uncategorized', $status_parameters)){
$query->orWhere(function ($query){
$query->whereNull('category_id');
});
}
});
// nlog($this->builder->toSql());
@ -200,7 +206,7 @@ class ExpenseFilters extends QueryFilters
return $this->builder->orderByRaw("REGEXP_REPLACE(number,'[^0-9]+','')+0 " . $dir);
}
if (is_array($sort_col) && in_array($sort_col[1], ['asc', 'desc']) && in_array($sort_col[0], ['public_notes', 'date', 'id_number', 'custom_value1', 'custom_value2', 'custom_value3', 'custom_value4'])) {
if (is_array($sort_col) && in_array($sort_col[1], ['asc', 'desc']) && in_array($sort_col[0], ['amount', 'public_notes', 'date', 'id_number', 'custom_value1', 'custom_value2', 'custom_value3', 'custom_value4'])) {
return $this->builder->orderBy($sort_col[0], $sort_col[1]);
}

View File

@ -227,8 +227,14 @@ class InvoiceFilters extends QueryFilters
if (is_numeric($date)) {
$date = Carbon::createFromTimestamp((int)$date);
} else {
try{
$date = Carbon::parse($date);
}
catch(\Exception $e){
return $this->builder;
}
}
return $this->builder->where('date', '>=', $date);
}

View File

@ -146,14 +146,14 @@ class RecurringExpenseFilters extends QueryFilters
return $this->builder
->orderByRaw('ISNULL(client_id), client_id '. $sort_col[1])
->orderBy(\App\Models\Client::select('name')
->whereColumn('clients.id', 'expenses.client_id'), $sort_col[1]);
->whereColumn('clients.id', 'recurring_expenses.client_id'), $sort_col[1]);
}
if ($sort_col[0] == 'vendor_id' && in_array($sort_col[1], ['asc', 'desc'])) {
return $this->builder
->orderByRaw('ISNULL(vendor_id), vendor_id '. $sort_col[1])
->orderBy(\App\Models\Vendor::select('name')
->whereColumn('vendors.id', 'expenses.vendor_id'), $sort_col[1]);
->whereColumn('vendors.id', 'recurring_expenses.vendor_id'), $sort_col[1]);
}
@ -161,7 +161,7 @@ class RecurringExpenseFilters extends QueryFilters
return $this->builder
->orderByRaw('ISNULL(category_id), category_id '. $sort_col[1])
->orderBy(\App\Models\ExpenseCategory::select('name')
->whereColumn('expense_categories.id', 'expenses.category_id'), $sort_col[1]);
->whereColumn('expense_categories.id', 'recurring_expenses.category_id'), $sort_col[1]);
}
if($sort_col[0] == 'number') {

View File

@ -133,6 +133,10 @@ class RecurringInvoiceFilters extends QueryFilters
return $this->builder->orderByRaw("REGEXP_REPLACE(number,'[^0-9]+','')+0 " . $dir);
}
if($sort_col[0] == 'next_send_datetime'){
$sort_col[0] = 'next_send_date';
}
return $this->builder->orderBy($sort_col[0], $dir);
}

View File

@ -191,7 +191,11 @@ class TransactionTransformer implements BankRevenueInterface
$date_format_default = $date_format->format;
}
try {
return Carbon::createFromFormat("d-m-Y", $input)->setTimezone($timezone_name)->format($date_format_default) ?? $input;
} catch (\Exception $e) {
return $input;
}
}
}

View File

@ -59,13 +59,13 @@ class EpcQrGenerator
<rect x='0' y='0' width='100%'' height='100%' />{$qr}</svg>";
} catch(\Throwable $e) {
nlog("EPC QR failure => ".$e->getMessage());
// nlog("EPC QR failure => ".$e->getMessage());
return '';
} catch(\Exception $e) {
nlog("EPC QR failure => ".$e->getMessage());
// nlog("EPC QR failure => ".$e->getMessage());
return '';
} catch(InvalidArgumentException $e) {
nlog("EPC QR failure => ".$e->getMessage());
// nlog("EPC QR failure => ".$e->getMessage());
return '';
}

View File

@ -44,3 +44,29 @@ function nlog($output, $context = []): void
$output = null;
$context = null;
}
function nrlog($output, $context = []): void
{
if (! config('ninja.expanded_logging')) {
return;
}
if (gettype($output) == 'object') {
$output = print_r($output, 1);
}
// $trace = debug_backtrace();
if (Ninja::isHosted()) {
try {
info($output);
} catch (\Exception $e) {
}
} else {
\Illuminate\Support\Facades\Log::channel('invoiceninja-reminders')->info($output, $context);
}
$output = null;
$context = null;
}

View File

@ -35,7 +35,7 @@ trait CustomValuer
return 0;
}
public function multiInclusiveTax($value, $has_custom_invoice_taxes) {
public function multiInclusiveTax($custom_value, $has_custom_invoice_taxes) {
if (isset($custom_value) && is_numeric($custom_value) && $has_custom_invoice_taxes !== false) {

View File

@ -245,12 +245,11 @@ class InvoiceSum
*/
private function setCalculatedAttributes(): self
{
/* If amount != balance then some money has been paid on the invoice, need to subtract this difference from the total to set the new balance */
if ($this->invoice->status_id != Invoice::STATUS_DRAFT) {
if($this->invoice->status_id == Invoice::STATUS_CANCELLED){
$this->invoice->balance = 0;
}
elseif ($this->invoice->status_id != Invoice::STATUS_DRAFT) {
if ($this->invoice->amount != $this->invoice->balance) {
// $paid_to_date = $this->invoice->amount - $this->invoice->balance;
$this->invoice->balance = Number::roundValue($this->getTotal(), $this->precision) - $this->invoice->paid_to_date; //21-02-2024 cannot use the calculated $paid_to_date here as it could send the balance backward.
} else {
$this->invoice->balance = Number::roundValue($this->getTotal(), $this->precision);

View File

@ -11,14 +11,14 @@
namespace App\Helpers\Invoice;
use App\Models\Quote;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\PurchaseOrder;
use App\Models\Quote;
use App\Models\RecurringInvoice;
use App\Models\RecurringQuote;
use App\Utils\Traits\NumberFormatter;
use App\Models\RecurringInvoice;
use Illuminate\Support\Collection;
use App\Utils\Traits\NumberFormatter;
class InvoiceSumInclusive
{
@ -74,8 +74,8 @@ class InvoiceSumInclusive
{
$this->calculateLineItems()
->calculateDiscount()
->calculateCustomValues()
->calculateInvoiceTaxes()
->calculateCustomValues()
->setTaxMap()
->calculateTotals() //just don't add the taxes!!
->calculateBalance()
@ -119,7 +119,6 @@ class InvoiceSumInclusive
$this->total_taxes += $this->multiInclusiveTax($this->invoice->custom_surcharge4, $this->invoice->custom_surcharge_tax4);
$this->total_custom_values += $this->valuer($this->invoice->custom_surcharge4);
$this->total += $this->total_custom_values;
return $this;
@ -138,21 +137,21 @@ class InvoiceSumInclusive
}
//Handles cases where the surcharge is not taxed
if(is_numeric($this->invoice->custom_surcharge1) && $this->invoice->custom_surcharge1 > 0 && $this->invoice->custom_surcharge_tax1) {
$amount += $this->invoice->custom_surcharge1;
}
// if(is_numeric($this->invoice->custom_surcharge1) && $this->invoice->custom_surcharge1 > 0 && !$this->invoice->custom_surcharge_tax1) {
// $amount += $this->invoice->custom_surcharge1;
// }
if(is_numeric($this->invoice->custom_surcharge2) && $this->invoice->custom_surcharge2 > 0 && $this->invoice->custom_surcharge_tax2) {
$amount += $this->invoice->custom_surcharge2;
}
// if(is_numeric($this->invoice->custom_surcharge2) && $this->invoice->custom_surcharge2 > 0 && !$this->invoice->custom_surcharge_tax2) {
// $amount += $this->invoice->custom_surcharge2;
// }
if(is_numeric($this->invoice->custom_surcharge3) && $this->invoice->custom_surcharge3 > 0 && $this->invoice->custom_surcharge_tax3) {
$amount += $this->invoice->custom_surcharge3;
}
// if(is_numeric($this->invoice->custom_surcharge3) && $this->invoice->custom_surcharge3 > 0 && !$this->invoice->custom_surcharge_tax3) {
// $amount += $this->invoice->custom_surcharge3;
// }
if(is_numeric($this->invoice->custom_surcharge4) && $this->invoice->custom_surcharge4 > 0 && $this->invoice->custom_surcharge_tax4) {
$amount += $this->invoice->custom_surcharge4;
}
// if(is_numeric($this->invoice->custom_surcharge4) && $this->invoice->custom_surcharge4 > 0 && !$this->invoice->custom_surcharge_tax4) {
// $amount += $this->invoice->custom_surcharge4;
// }
if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) > 1) {
$tax = $this->calcInclusiveLineTax($this->invoice->tax_rate1, $amount);
@ -280,10 +279,11 @@ class InvoiceSumInclusive
private function setCalculatedAttributes()
{
/* If amount != balance then some money has been paid on the invoice, need to subtract this difference from the total to set the new balance */
if ($this->invoice->status_id != Invoice::STATUS_DRAFT) {
if($this->invoice->status_id == Invoice::STATUS_CANCELLED){
$this->invoice->balance = 0;
}
elseif ($this->invoice->status_id != Invoice::STATUS_DRAFT) {
if ($this->invoice->amount != $this->invoice->balance) {
// $paid_to_date = $this->invoice->amount - $this->invoice->balance;
$this->invoice->balance = $this->formatValue($this->getTotal(), $this->precision) - $this->invoice->paid_to_date;
} else {
$this->invoice->balance = $this->formatValue($this->getTotal(), $this->precision);

View File

@ -178,13 +178,13 @@ class SwissQrGenerator
if(is_iterable($qrBill->getViolations())) {
foreach ($qrBill->getViolations() as $key => $violation) {
nlog("qr");
nlog($violation);
// nlog("qr");
// nlog($violation);
}
}
nlog($e->getMessage());
// nlog($e->getMessage());
return '';
// return $e->getMessage();

View File

@ -165,7 +165,9 @@ class ContactLoginController extends Controller
private function setRedirectPath()
{
if (auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_INVOICES) {
if (auth()->guard('contact')->user()->client->getSetting('enable_client_portal_dashboard') === true) {
$this->redirectTo = '/client/dashboard';
} elseif (auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_INVOICES) {
$this->redirectTo = '/client/invoices';
} elseif (auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_RECURRING_INVOICES) {
$this->redirectTo = '/client/recurring_invoices';

View File

@ -11,36 +11,37 @@
namespace App\Http\Controllers;
use App\Models\Account;
use App\Models\BankIntegration;
use App\Models\BankTransaction;
use App\Models\BankTransactionRule;
use App\Models\User;
use App\Utils\Ninja;
use App\Models\Client;
use App\Models\CompanyGateway;
use App\Models\Design;
use App\Models\ExpenseCategory;
use App\Models\GroupSetting;
use App\Models\PaymentTerm;
use App\Utils\Statics;
use App\Models\Account;
use App\Models\TaxRate;
use App\Models\Webhook;
use App\Models\Scheduler;
use App\Models\TaskStatus;
use App\Models\TaxRate;
use App\Models\User;
use App\Models\Webhook;
use App\Transformers\ArraySerializer;
use App\Transformers\EntityTransformer;
use App\Utils\Ninja;
use App\Utils\Statics;
use App\Utils\Traits\AppSetup;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use App\Models\PaymentTerm;
use Illuminate\Support\Str;
use League\Fractal\Manager;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection;
use App\Models\GroupSetting;
use Illuminate\Http\Response;
use App\Models\CompanyGateway;
use App\Utils\Traits\AppSetup;
use App\Models\BankIntegration;
use App\Models\BankTransaction;
use App\Models\ExpenseCategory;
use League\Fractal\Resource\Item;
use App\DataMapper\EDoc\Schema\RO;
use App\Models\BankTransactionRule;
use Illuminate\Support\Facades\Auth;
use App\Transformers\ArraySerializer;
use App\Transformers\EntityTransformer;
use League\Fractal\Resource\Collection;
use Illuminate\Database\Eloquent\Builder;
use League\Fractal\Serializer\JsonApiSerializer;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use Illuminate\Contracts\Container\BindingResolutionException;
/**
* Class BaseController.
@ -276,7 +277,7 @@ class BaseController extends Controller
/**
* API Error response.
*
* @param string $message The return error message
* @param string|array $message The return error message
* @param int $httpErrorCode 404/401/403 etc
* @return Response The JSON response
* @throws BindingResolutionException
@ -993,7 +994,17 @@ class BaseController extends Controller
/** @var \App\Models\User $user */
$user = auth()->user();
$response['static'] = Statics::company($user->getCompany()->getLocale());
$response_data = Statics::company($user->getCompany()->getLocale());
if(request()->has('einvoice')){
$ro = new RO();
$response_data['einvoice_schema'] = $ro();
}
$response['static'] = $response_data;
}
}

View File

@ -258,6 +258,18 @@ class ClientController extends BaseController
}
if($action == 'bulk_update' && $user->can('edit', $clients->first())){
$clients = Client::withTrashed()
->company()
->whereIn('id', $request->ids);
$this->client_repo->bulkUpdate($clients, $request->column, $request->new_value);
return $this->listResponse(Client::query()->withTrashed()->company()->whereIn('id', $request->ids));
}
$clients->each(function ($client) use ($action, $user) {
if ($user->can('edit', $client)) {
$this->client_repo->{$action}($client);

View File

@ -178,6 +178,9 @@ class QuoteController extends Controller
->where('client_id', auth()->guard('contact')->user()->client->id)
->where('company_id', auth()->guard('contact')->user()->client->company_id)
->whereIn('status_id', [Quote::STATUS_DRAFT, Quote::STATUS_SENT])
->where(function ($q){
$q->whereNull('due_date')->orWhere('due_date', '>=', now());
})
->withTrashed()
->get();

View File

@ -705,8 +705,25 @@ class CompanyController extends BaseController
$logo = strlen($company->settings->company_logo) > 5 ? $company->settings->company_logo : 'https://pdf.invoicing.co/favicon-v2.png';
$headers = ['Content-Disposition' => 'inline'];
try{
$response = \Illuminate\Support\Facades\Http::get($logo);
if ($response->successful()) {
$logo = $response->body();
}
else {
$logo = base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=');
}
}
catch(\Exception $e){
$logo = base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=');
}
return response()->streamDownload(function () use ($logo) {
echo @file_get_contents($logo);
echo $logo;
}, 'logo.png', $headers);
}

View File

@ -565,7 +565,6 @@ class CompanyGatewayController extends BaseController
public function importCustomers(TestCompanyGatewayRequest $request, CompanyGateway $company_gateway)
{
// $x = Cache::pull("throttle_polling:import_customers:{$company_gateway->company->company_key}:{$company_gateway->hashed_id}");
//Throttle here
if (Cache::has("throttle_polling:import_customers:{$company_gateway->company->company_key}:{$company_gateway->hashed_id}"))

View File

@ -121,7 +121,8 @@ class DocumentController extends BaseController
}
return response()->streamDownload(function () use ($document) {
echo file_get_contents($document->generateUrl());
// echo file_get_contents($document->generateUrl());
echo $document->getFile();
}, basename($document->generateUrl()), $headers);
}

View File

@ -70,7 +70,7 @@ class EmailController extends BaseController
/** @var \App\Models\User $user */
$user = auth()->user();
if ($request->cc_email && (Ninja::isSelfHost() || $user->account->isPaidHostedClient())) {
if ($request->cc_email && (Ninja::isSelfHost() || $user->account->isPremium())) {
foreach($request->cc_email as $email) {
$mo->cc[] = new Address($email);

View File

@ -167,11 +167,11 @@ class ImportController extends Controller
private function convertEncoding($data)
{
$enc = mb_detect_encoding($data, mb_list_encodings(), true);
// $enc = mb_detect_encoding($data, mb_list_encodings(), true);
if($enc !== false) {
$data = mb_convert_encoding($data, "UTF-8", $enc);
}
// if($enc !== false) {
// $data = mb_convert_encoding($data, "UTF-8", $enc);
// }
return $data;
}
@ -233,9 +233,51 @@ class ImportController extends Controller
}
}
return $this->convertData($data);
}
private function convertData(array $data): array
{
// List of encodings to check against
$encodings = [
'UTF-8',
'ISO-8859-1', // Latin-1
'ISO-8859-2', // Latin-2
'WINDOWS-1252', // CP1252
'SHIFT-JIS',
'EUC-JP',
'GB2312',
'GBK',
'BIG5',
'ISO-2022-JP',
'KOI8-R',
'KOI8-U',
'WINDOWS-1251', // CP1251
'UTF-16',
'UTF-32',
'ASCII'
];
foreach ($data as $key => $value) {
// Only process strings
if (is_string($value)) {
// Detect the encoding of the string
$detectedEncoding = mb_detect_encoding($value, $encodings, true);
// If encoding is detected and it's not UTF-8, convert it to UTF-8
if ($detectedEncoding && $detectedEncoding !== 'UTF-8') {
$array[$key] = mb_convert_encoding($value, 'UTF-8', $detectedEncoding);
}
}
}
return $data;
}
/**
* Returns the best delimiter
*

View File

@ -64,6 +64,9 @@ class SelfUpdateController extends BaseController
$file_headers = @get_headers($this->getDownloadUrl());
if(!is_array($file_headers))
return response()->json(['message' => 'There was a problem reaching the update server, please try again in a little while.'], 410);
if (stripos($file_headers[0], "404 Not Found") > 0 || (stripos($file_headers[0], "302 Found") > 0 && stripos($file_headers[7], "404 Not Found") > 0)) {
return response()->json(['message' => 'Download not yet available. Please try again shortly.'], 410);
}
@ -99,6 +102,9 @@ class SelfUpdateController extends BaseController
}
}
if(Storage::disk('base')->directoryExists('resources/lang'))
Storage::disk('base')->deleteDirectory('resources/lang');
nlog('Removing cache files');
Artisan::call('clear-compiled');

View File

@ -13,6 +13,7 @@ namespace App\Http\Controllers;
use App\Utils\Statics;
use Illuminate\Http\Response;
use Invoiceninja\Einvoice\Decoder\Schema;
class StaticController extends BaseController
{
@ -56,8 +57,14 @@ class StaticController extends BaseController
/** @var \App\Models\User $user */
$user = auth()->user();
$response = Statics::company($user->getLocale() ?? $user->company()->getLocale());
$response_data = Statics::company($user->getLocale() ?? $user->company()->getLocale());
return response()->json($response, 200, ['Content-type' => 'application/json; charset=utf-8'], JSON_PRETTY_PRINT);
if(request()->has('einvoice')){
$schema = new Schema();
$response_data['einvoice_schema'] = $schema('FACT1');
}
return response()->json($response_data, 200, ['Content-type' => 'application/json; charset=utf-8'], JSON_PRETTY_PRINT);
}
}

View File

@ -37,18 +37,18 @@ class StripeConnectController extends BaseController
MultiDB::findAndSetDbByCompanyKey($request->getTokenContent()['company_key']);
$company_gateway = CompanyGateway::query()
->where('gateway_key', 'd14dd26a47cecc30fdd65700bfb67b34')
->where('company_id', $request->getCompany()->id)
->first();
// $company_gateway = CompanyGateway::query()
// ->where('gateway_key', 'd14dd26a47cecc30fdd65700bfb67b34')
// ->where('company_id', $request->getCompany()->id)
// ->first();
if ($company_gateway) {
$config = $company_gateway->getConfig();
// if ($company_gateway) {
// $config = $company_gateway->getConfig();
if (property_exists($config, 'account_id') && strlen($config->account_id) > 5) {
return view('auth.connect.existing');
}
}
// if (property_exists($config, 'account_id') && strlen($config->account_id) > 5) {
// return view('auth.connect.existing');
// }
// }
$stripe_client_id = config('ninja.ninja_stripe_client_id');
$redirect_uri = config('ninja.app_url').'/stripe/completed';
@ -127,7 +127,6 @@ class StripeConnectController extends BaseController
'refresh_token' => $response->refresh_token,
'access_token' => $response->access_token,
'appleDomainVerification' => '',
// "statementDescriptor" => "",
];
$company_gateway->setConfig($payload);
@ -145,9 +144,6 @@ class StripeConnectController extends BaseController
nlog("could not harvest stripe company name");
}
// nlog("Stripe Connect Redirect URI = {$redirect_uri}");
// StripeWebhook::dispatch($company->company_key, $company_gateway->id);
if(isset($request->getTokenContent()['is_react']) && $request->getTokenContent()['is_react']) {
$redirect_uri = config('ninja.react_url').'/#/settings/online_payments';
} else {
@ -158,7 +154,7 @@ class StripeConnectController extends BaseController
//response here
return view('auth.connect.completed', ['url' => $redirect_uri]);
// return redirect($redirect_uri);
}
}

View File

@ -537,16 +537,6 @@ class TaskController extends BaseController
return $this->listResponse(Task::withTrashed()->whereIn('id', $this->transformKeys($ids)));
}
/**
* Returns a client statement.
*
* @return void [type] [description]
*/
public function statement()
{
//todo
}
/**
* Update the specified resource in storage.
*

View File

@ -129,6 +129,10 @@ class TwilioController extends BaseController
$user->verified_phone_number = true;
$user->save();
if (class_exists(\Modules\Admin\Jobs\Account\UserQualityCheck::class)) {
\Modules\Admin\Jobs\Account\UserQualityCheck::dispatch($user, $user->company()->db);
}
return response()->json(['message' => 'SMS verified'], 200);
}

View File

@ -161,7 +161,9 @@ class ContactKeyLogin
private function setRedirectPath()
{
if (auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_INVOICES) {
if (auth()->guard('contact')->user()->client->getSetting('enable_client_portal_dashboard') === true) {
return '/client/dashboard';
} elseif (auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_INVOICES) {
return '/client/invoices';
} elseif (auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_RECURRING_INVOICES) {
return '/client/recurring_invoices';

View File

@ -41,7 +41,7 @@ class StoreBankTransactionRequest extends Request
$rules = [];
$rules['bank_integration_id'] = 'bail|required|exists:bank_integrations,id,company_id,'.$user->company()->id.',is_deleted,0';
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'nullable', 'max:99999999999999'];
return $rules;
}
@ -55,6 +55,7 @@ class StoreBankTransactionRequest extends Request
$input['bank_integration_id'] = $this->decodePrimaryKey($input['bank_integration_id']);
}
$this->replace($input);
}
}

View File

@ -44,9 +44,7 @@ class UpdateBankTransactionRequest extends Request
$rules['vendor_id'] = 'bail|required|exists:vendors,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
}
// if (isset($this->expense_id)) {
// $rules['expense_id'] = 'bail|required|exists:expenses,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
// }
$rules['amount'] = ['sometimes', 'bail', 'nullable', 'numeric', 'max:99999999999999'];
$rules['bank_integration_id'] = 'bail|required|exists:bank_integrations,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';

View File

@ -35,12 +35,14 @@ class BulkClientRequest extends Request
$user = auth()->user();
return [
'action' => 'required|string|in:archive,restore,delete,template,assign_group',
'action' => 'required|string|in:archive,restore,delete,template,assign_group,bulk_update',
'ids' => ['required','bail','array',Rule::exists('clients', 'id')->where('company_id', $user->company()->id)],
'template' => 'sometimes|string',
'template_id' => 'sometimes|string',
'group_settings_id' => ['required_if:action,assign_group',Rule::exists('group_settings', 'id')->where('company_id', $user->company()->id)],
'send_email' => 'sometimes|bool'
'send_email' => 'sometimes|bool',
'column' => ['required_if:action,bulk_update', 'string', Rule::in(\App\Models\Client::$bulk_update_columns)],
'new_value' => ['required_if:action,bulk_update|string'],
];
}

View File

@ -144,9 +144,12 @@ class UpdateCompanyRequest extends Request
}
if (isset($settings['email_style_custom'])) {
$settings['email_style_custom'] = str_replace(['{!!', '!!}', '{{', '}}', '@if(', '@endif', '@isset', '@unless', '@auth', '@empty', '@guest', '@env', '@section', '@switch', '@foreach', '@while', '@include', '@each', '@once', '@push', '@use', '@forelse', '@verbatim', '<?php', '@php', '@for'], '', $settings['email_style_custom']);
$settings['email_style_custom'] = str_replace(['{!!', '!!}', '{{', '}}', '@checked', '@dd', '@dump', '@if', '@if(', '@endif', '@isset', '@unless', '@auth', '@empty', '@guest', '@env', '@section', '@switch', '@foreach', '@while', '@include', '@each', '@once', '@push', '@use', '@forelse', '@verbatim', '<?php', '@php', '@for', '@class', '</sc', '<sc', 'html;base64', '@elseif', '@else', '@endunless', '@endisset', '@endempty', '@endauth', '@endguest', '@endproduction', '@endenv', '@hasSection', '@endhasSection', '@sectionMissing', '@endsectionMissing', '@endfor', '@endforeach', '@empty', '@endforelse', '@endwhile', '@continue', '@break', '@includeIf', '@includeWhen', '@includeUnless', '@includeFirst', '@component', '@endcomponent', '@endsection', '@yield', '@show', '@append', '@overwrite', '@stop', '@extends', '@endpush', '@stack', '@prepend', '@endprepend', '@slot', '@endslot', '@endphp', '@method', '@csrf', '@error', '@enderror', '@json', '@endverbatim', '@inject'], '', $settings['email_style_custom']);
}
if (isset($settings['company_logo']) && strlen($settings['company_logo']) > 2)
$settings['company_logo'] = $this->forceScheme($settings['company_logo']);
if (!$account->isFreeHostedClient()) {
return $settings;
}
@ -171,4 +174,10 @@ class UpdateCompanyRequest extends Request
return rtrim($url, '/');
}
private function forceScheme($url)
{
return stripos($url, 'http') !== false ? $url : "https://{$url}";
}
}

View File

@ -67,7 +67,7 @@ class StoreCreditRequest extends Request
// $rules['number'] = new UniqueCreditNumberRule($this->all());
$rules['number'] = ['nullable', Rule::unique('credits')->where('company_id', $user->company()->id)];
$rules['discount'] = 'sometimes|numeric';
$rules['discount'] = 'sometimes|numeric|max:99999999999999';
$rules['is_amount_discount'] = ['boolean'];
$rules['tax_rate1'] = 'bail|sometimes|numeric';
$rules['tax_rate2'] = 'bail|sometimes|numeric';
@ -76,6 +76,9 @@ class StoreCreditRequest extends Request
$rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable';
$rules['exchange_rate'] = 'bail|sometimes|numeric';
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
$rules['date'] = 'bail|sometimes|date:Y-m-d';
if ($this->invoice_id) {
$rules['invoice_id'] = new ValidInvoiceCreditRule();
@ -101,6 +104,7 @@ class StoreCreditRequest extends Request
$input = $this->decodePrimaryKeys($input);
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
$input['amount'] = $this->entityTotalAmount($input['line_items']);
if (array_key_exists('exchange_rate', $input) && is_null($input['exchange_rate'])) {
$input['exchange_rate'] = 1;

View File

@ -67,7 +67,10 @@ class UpdateCreditRequest extends Request
$rules['client_id'] = ['bail', 'sometimes',Rule::in([$this->credit->client_id])];
$rules['line_items'] = 'array';
$rules['discount'] = 'sometimes|numeric';
$rules['date'] = 'bail|sometimes|date:Y-m-d';
$rules['discount'] = 'sometimes|numeric|max:99999999999999';
$rules['is_amount_discount'] = ['boolean'];
$rules['tax_rate1'] = 'bail|sometimes|numeric';
$rules['tax_rate2'] = 'bail|sometimes|numeric';
@ -76,6 +79,7 @@ class UpdateCreditRequest extends Request
$rules['tax_name2'] = 'bail|sometimes|string|nullable';
$rules['tax_name3'] = 'bail|sometimes|string|nullable';
$rules['exchange_rate'] = 'bail|sometimes|numeric';
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
return $rules;
}
@ -92,6 +96,8 @@ class UpdateCreditRequest extends Request
if (isset($input['line_items'])) {
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
$input['amount'] = $this->entityTotalAmount($input['line_items']);
}
if (array_key_exists('exchange_rate', $input) && is_null($input['exchange_rate'])) {

View File

@ -53,6 +53,7 @@ class StoreExpenseRequest extends Request
$rules['payment_date'] = 'bail|nullable|sometimes|date:Y-m-d';
$rules['date'] = 'bail|sometimes|date:Y-m-d';
$rules['documents'] = 'bail|sometimes|array';
$rules['amount'] = ['sometimes', 'bail', 'nullable', 'numeric', 'max:99999999999999'];
return $this->globalRules($rules);
}

View File

@ -55,7 +55,7 @@ class UpdateExpenseRequest extends Request
$rules['transaction_id'] = 'bail|sometimes|nullable|exists:bank_transactions,id,company_id,'.$user->company()->id;
$rules['invoice_id'] = 'bail|sometimes|nullable|exists:invoices,id,company_id,'.$user->company()->id;
$rules['documents'] = 'bail|sometimes|array';
$rules['amount'] = ['sometimes', 'bail', 'nullable', 'numeric', 'max:99999999999999'];
return $this->globalRules($rules);
}

View File

@ -66,8 +66,10 @@ class StoreInvoiceRequest extends Request
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
$rules['is_amount_discount'] = ['boolean'];
$rules['date'] = 'bail|sometimes|date:Y-m-d';
$rules['line_items'] = 'array';
$rules['discount'] = 'sometimes|numeric';
$rules['discount'] = 'sometimes|numeric|max:99999999999999';
$rules['tax_rate1'] = 'bail|sometimes|numeric';
$rules['tax_rate2'] = 'bail|sometimes|numeric';
$rules['tax_rate3'] = 'bail|sometimes|numeric';
@ -77,8 +79,10 @@ class StoreInvoiceRequest extends Request
$rules['exchange_rate'] = 'bail|sometimes|numeric';
$rules['partial'] = 'bail|sometimes|nullable|numeric|gte:0';
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date'];
// $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
// $rules['amount'] = ['sometimes', 'bail', 'max:99999999999999'];
// $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
return $rules;
}
@ -89,17 +93,18 @@ class StoreInvoiceRequest extends Request
$input = $this->decodePrimaryKeys($input);
$input['amount'] = 0;
$input['balance'] = 0;
if (isset($input['line_items']) && is_array($input['line_items'])) {
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
$input['amount'] = $this->entityTotalAmount($input['line_items']);
}
if(isset($input['partial']) && $input['partial'] == 0) {
$input['partial_due_date'] = null;
}
$input['amount'] = 0;
$input['balance'] = 0;
if (array_key_exists('tax_rate1', $input) && is_null($input['tax_rate1'])) {
$input['tax_rate1'] = 0;
}

View File

@ -66,7 +66,8 @@ class UpdateInvoiceRequest extends Request
$rules['is_amount_discount'] = ['boolean'];
$rules['client_id'] = ['bail', 'sometimes', Rule::in([$this->invoice->client_id])];
$rules['line_items'] = 'array';
$rules['discount'] = 'sometimes|numeric';
$rules['discount'] = 'sometimes|numeric|max:99999999999999';
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
$rules['tax_rate1'] = 'bail|sometimes|numeric';
$rules['tax_rate2'] = 'bail|sometimes|numeric';
@ -77,6 +78,10 @@ class UpdateInvoiceRequest extends Request
$rules['status_id'] = 'bail|sometimes|not_in:5'; //do not allow cancelled invoices to be modfified.
$rules['exchange_rate'] = 'bail|sometimes|numeric';
$rules['partial'] = 'bail|sometimes|nullable|numeric';
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
$rules['date'] = 'bail|sometimes|date:Y-m-d';
// $rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date'];
// $rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
@ -97,6 +102,7 @@ class UpdateInvoiceRequest extends Request
if (isset($input['line_items']) && is_array($input['line_items'])) {
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
$input['amount'] = $this->entityTotalAmount($input['line_items']);
}
if (array_key_exists('documents', $input)) {

View File

@ -67,9 +67,8 @@ class RefundPaymentRequest extends Request
$input = $this->all();
$rules = [
'id' => 'bail|required', //@phpstan-ignore-line
'id' => new ValidRefundableRequest($input),
'amount' => 'numeric',
'id' => ['bail','required', new ValidRefundableRequest($input)],
'amount' => ['numeric', 'max:99999999999999'],
'date' => 'required',
'invoices.*.invoice_id' => 'required',
'invoices.*.amount' => 'required',

View File

@ -45,12 +45,12 @@ class StorePaymentRequest extends Request
$rules = [
'client_id' => ['bail','required',Rule::exists('clients','id')->where('company_id',$user->company()->id)->where('is_deleted', 0)],
'amount' => ['bail', 'numeric', new PaymentAmountsBalanceRule()],
'invoices' => ['bail','sometimes', 'nullable', 'array', new ValidPayableInvoicesRule()],
'invoices.*.amount' => ['bail','required'],
'invoices.*.invoice_id' => ['bail','required','distinct', new ValidInvoicesRules($this->all()),Rule::exists('invoices','id')->where('company_id', $user->company()->id)->where('client_id', $this->client_id)],
'credits.*.credit_id' => ['bail','required','distinct', new ValidCreditsRules($this->all()),Rule::exists('credits','id')->where('company_id', $user->company()->id)->where('client_id', $this->client_id)],
'credits.*.amount' => ['bail','required', new CreditsSumRule($this->all())],
'invoices' => ['bail','sometimes', 'nullable', 'array', new ValidPayableInvoicesRule()],
'amount' => ['bail', 'numeric', new PaymentAmountsBalanceRule(), 'max:99999999999999'],
'number' => ['bail', 'nullable', Rule::unique('payments')->where('company_id', $user->company()->id)],
'idempotency_key' => ['nullable', 'bail', 'string','max:64', Rule::unique('payments')->where('company_id', $user->company()->id)],
];
@ -94,7 +94,7 @@ class StorePaymentRequest extends Request
if (isset($input['invoices']) && is_array($input['invoices']) !== false) {
foreach ($input['invoices'] as $key => $value) {
if (is_string($value['invoice_id'])) {
if (isset($value['invoice_id']) && is_string($value['invoice_id'])) {
$input['invoices'][$key]['invoice_id'] = $this->decodePrimaryKey($value['invoice_id']);
}
@ -110,7 +110,7 @@ class StorePaymentRequest extends Request
if (isset($input['credits']) && is_array($input['credits']) !== false) {
foreach ($input['credits'] as $key => $value) {
if (array_key_exists('credit_id', $input['credits'][$key])) {
if (isset($value['credit_id']) && is_string($value['credit_id'])) {
$input['credits'][$key]['credit_id'] = $this->decodePrimaryKey($value['credit_id']);
$credits_total += $value['amount'];
}

View File

@ -49,7 +49,8 @@ class StorePurchaseOrderRequest extends Request
$rules['vendor_id'] = 'bail|required|exists:vendors,id,company_id,'.$user->company()->id.',is_deleted,0';
$rules['number'] = ['nullable', Rule::unique('purchase_orders')->where('company_id', $user->company()->id)];
$rules['discount'] = 'sometimes|numeric';
$rules['discount'] = 'sometimes|numeric|max:99999999999999';
$rules['is_amount_discount'] = ['boolean'];
$rules['line_items'] = 'array';
@ -70,6 +71,8 @@ class StorePurchaseOrderRequest extends Request
$rules['status_id'] = 'nullable|integer|in:1,2,3,4,5';
$rules['exchange_rate'] = 'bail|sometimes|numeric';
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
return $rules;
}
@ -79,16 +82,18 @@ class StorePurchaseOrderRequest extends Request
$input = $this->decodePrimaryKeys($input);
$input['amount'] = 0;
$input['balance'] = 0;
if(isset($input['partial']) && $input['partial'] == 0) {
$input['partial_due_date'] = null;
}
if (isset($input['line_items']) && is_array($input['line_items'])) {
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
}
$input['amount'] = $this->entityTotalAmount($input['line_items']);
$input['amount'] = 0;
$input['balance'] = 0;
}
if (array_key_exists('exchange_rate', $input) && is_null($input['exchange_rate'])) {
$input['exchange_rate'] = 1;

View File

@ -52,7 +52,8 @@ class UpdatePurchaseOrderRequest extends Request
$rules['vendor_id'] = ['bail', 'sometimes', Rule::in([$this->purchase_order->vendor_id])];
$rules['line_items'] = 'array';
$rules['discount'] = 'sometimes|numeric';
$rules['discount'] = 'sometimes|numeric|max:99999999999999';
$rules['is_amount_discount'] = ['boolean'];
if ($this->file('documents') && is_array($this->file('documents'))) {
@ -71,6 +72,7 @@ class UpdatePurchaseOrderRequest extends Request
$rules['status_id'] = 'sometimes|integer|in:1,2,3,4,5';
$rules['exchange_rate'] = 'bail|sometimes|numeric';
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
return $rules;
}
@ -89,6 +91,7 @@ class UpdatePurchaseOrderRequest extends Request
if (isset($input['line_items']) && is_array($input['line_items'])) {
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
$input['amount'] = $this->entityTotalAmount($input['line_items']);
}
if (array_key_exists('exchange_rate', $input) && is_null($input['exchange_rate'])) {

View File

@ -60,12 +60,15 @@ class StoreQuoteRequest extends Request
}
$rules['number'] = ['nullable', Rule::unique('quotes')->where('company_id', $user->company()->id)];
$rules['discount'] = 'sometimes|numeric';
$rules['discount'] = 'sometimes|numeric|max:99999999999999';
$rules['is_amount_discount'] = ['boolean'];
$rules['exchange_rate'] = 'bail|sometimes|numeric';
$rules['line_items'] = 'array';
$rules['date'] = 'bail|sometimes|date:Y-m-d';
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date', 'after_or_equal:date'];
$rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
return $rules;
}
@ -79,10 +82,14 @@ class StoreQuoteRequest extends Request
$input = $this->decodePrimaryKeys($input);
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
$input['amount'] = 0;
$input['balance'] = 0;
if (isset($input['line_items']) && is_array($input['line_items'])) {
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
$input['amount'] = $this->entityTotalAmount($input['line_items']);
}
if (array_key_exists('exchange_rate', $input) && is_null($input['exchange_rate'])) {
$input['exchange_rate'] = 1;
}

View File

@ -59,12 +59,15 @@ class UpdateQuoteRequest extends Request
$rules['number'] = ['bail', 'sometimes', 'nullable', Rule::unique('quotes')->where('company_id', $user->company()->id)->ignore($this->quote->id)];
$rules['client_id'] = ['bail', 'sometimes', Rule::in([$this->quote->client_id])];
$rules['line_items'] = 'array';
$rules['discount'] = 'sometimes|numeric';
$rules['discount'] = 'sometimes|numeric|max:99999999999999';
$rules['is_amount_discount'] = ['boolean'];
$rules['exchange_rate'] = 'bail|sometimes|numeric';
$rules['date'] = 'bail|sometimes|date:Y-m-d';
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date'];
$rules['due_date'] = ['bail', 'sometimes', 'nullable', 'after:partial_due_date', 'after_or_equal:date', Rule::requiredIf(fn () => strlen($this->partial_due_date) > 1), 'date'];
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
return $rules;
}
@ -79,6 +82,7 @@ class UpdateQuoteRequest extends Request
if (isset($input['line_items'])) {
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
$input['amount'] = $this->entityTotalAmount($input['line_items']);
}
if (array_key_exists('documents', $input)) {

View File

@ -79,6 +79,8 @@ class StoreRecurringInvoiceRequest extends Request
$rules['exchange_rate'] = 'bail|sometimes|numeric';
$rules['next_send_date'] = 'bail|required|date|after:yesterday';
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
return $rules;
}
@ -145,6 +147,7 @@ class StoreRecurringInvoiceRequest extends Request
}
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
$input['amount'] = $this->entityTotalAmount($input['line_items']);
if (isset($input['auto_bill'])) {
$input['auto_bill_enabled'] = $this->setAutoBillFlag($input['auto_bill']);

View File

@ -72,6 +72,7 @@ class UpdateRecurringInvoiceRequest extends Request
$rules['tax_name3'] = 'bail|sometimes|string|nullable';
$rules['exchange_rate'] = 'bail|sometimes|numeric';
$rules['next_send_date'] = 'bail|required|date|after:yesterday';
$rules['amount'] = ['sometimes', 'bail', 'numeric', 'max:99999999999999'];
return $rules;
}
@ -126,6 +127,7 @@ class UpdateRecurringInvoiceRequest extends Request
if (isset($input['line_items'])) {
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
$input['amount'] = $this->entityTotalAmount($input['line_items']);
}
if (array_key_exists('auto_bill', $input) && isset($input['auto_bill'])) {

View File

@ -21,6 +21,8 @@ class BlackListRule implements ValidationRule
{
/** Bad domains +/- dispoable email domains */
private array $blacklist = [
'anonaddy.me',
'nqmo.com',
'wireconnected.com',
'secure-coinspot.com',
'casasotombo.com',

View File

@ -39,20 +39,6 @@ class CanAddUserRule implements Rule
return true;
}
/*
Check that we have sufficient quota to allow this to happen
@ 31-01-2024 - changed query to use email instead of user_id
$count = CompanyUser::query()
->where('company_user.account_id', $user->account_id)
->join('users', 'users.id', '=', 'company_user.user_id')
->whereNull('users.deleted_at')
->whereNull('company_user.deleted_at')
->distinct()
->count('company_user.user_id');
*/
$count = CompanyUser::query()
->where("company_user.account_id", $user->account_id)
->join("users", "users.id", "=", "company_user.user_id")

View File

@ -937,4 +937,44 @@ class BaseImport
return $data;
}
private function convertData(array $data): array
{
// List of encodings to check against
$encodings = [
'UTF-8',
'ISO-8859-1', // Latin-1
'ISO-8859-2', // Latin-2
'WINDOWS-1252', // CP1252
'SHIFT-JIS',
'EUC-JP',
'GB2312',
'GBK',
'BIG5',
'ISO-2022-JP',
'KOI8-R',
'KOI8-U',
'WINDOWS-1251', // CP1251
'UTF-16',
'UTF-32',
'ASCII'
];
foreach ($data as $key => $value) {
// Only process strings
if (is_string($value)) {
// Detect the encoding of the string
$detectedEncoding = mb_detect_encoding($value, $encodings, true);
// If encoding is detected and it's not UTF-8, convert it to UTF-8
if ($detectedEncoding && $detectedEncoding !== 'UTF-8') {
$array[$key] = mb_convert_encoding($value, 'UTF-8', $detectedEncoding);
}
}
}
return $data;
}
}

View File

@ -42,7 +42,7 @@ class ExpenseTransformer extends BaseTransformer
'client_id' => isset($data['expense.client'])
? $this->getClientId($data['expense.client'])
: null,
'date' => strlen($this->getString($data, 'expense.date') > 1) ? date('Y-m-d', strtotime(str_replace("/", "-", $data['expense.date']))) : now()->format('Y-m-d'),
'date' => strlen($this->getString($data, 'expense.date') > 1) ? $this->parseDate($data['expense.date']) : now()->format('Y-m-d'),
'public_notes' => $this->getString($data, 'expense.public_notes'),
'private_notes' => $this->getString($data, 'expense.private_notes'),
'category_id' => isset($data['expense.category'])
@ -55,7 +55,7 @@ class ExpenseTransformer extends BaseTransformer
? $this->getPaymentTypeId($data['expense.payment_type'])
: null,
'payment_date' => isset($data['expense.payment_date'])
? date('Y-m-d', strtotime(str_replace("/", "-", $data['expense.payment_date'])))
? $this->parseDate($data['expense.payment_date'])
: null,
'custom_value1' => $this->getString($data, 'expense.custom_value1'),
'custom_value2' => $this->getString($data, 'expense.custom_value2'),

View File

@ -93,11 +93,8 @@ class InvoiceTransformer extends BaseTransformer
'invoice.custom_value4'
),
'footer' => $this->getString($invoice_data, 'invoice.footer'),
'partial' => $this->getFloat($invoice_data, 'invoice.partial') > 0 ?: null,
'partial_due_date' => $this->getString(
$invoice_data,
'invoice.partial_due_date'
),
'partial' => $this->getFloat($invoice_data, 'invoice.partial') > 0 ? $this->getFloat($invoice_data, 'invoice.partial') : null,
'partial_due_date' => isset($invoice_data['invoice.partial_due_date']) ? $this->parseDate($invoice_data['invoice.partial_due_date']) : null,
'custom_surcharge1' => $this->getFloat(
$invoice_data,
'invoice.custom_surcharge1'

View File

@ -56,10 +56,10 @@ class QuoteTransformer extends BaseTransformer
'discount' => $this->getFloat($quote_data, 'quote.discount'),
'po_number' => $this->getString($quote_data, 'quote.po_number'),
'date' => isset($quote_data['quote.date'])
? date('Y-m-d', strtotime(str_replace("/", "-", $quote_data['quote.date'])))
? $this->parseDate($quote_data['quote.date'])
: now()->format('Y-m-d'),
'due_date' => isset($quote_data['quote.due_date'])
? date('Y-m-d', strtotime(str_replace("/", "-", $quote_data['quote.due_date'])))
? $this->parseDate($quote_data['quote.due_date'])
: null,
'terms' => $this->getString($quote_data, 'quote.terms'),
'public_notes' => $this->getString(
@ -94,10 +94,7 @@ class QuoteTransformer extends BaseTransformer
),
'footer' => $this->getString($quote_data, 'quote.footer'),
'partial' => $this->getFloat($quote_data, 'quote.partial'),
'partial_due_date' => $this->getString(
$quote_data,
'quote.partial_due_date'
),
'partial_due_date' => isset($invoice_data['quote.partial_due_date']) ? $this->parseDate($quote_data['quote.partial_due_date']) : null,
'custom_surcharge1' => $this->getString(
$quote_data,
'quote.custom_surcharge1'
@ -139,10 +136,7 @@ class QuoteTransformer extends BaseTransformer
$transformed['payments'] = [
[
'date' => isset($quote_data['payment.date'])
? date(
'Y-m-d',
strtotime($quote_data['payment.date'])
)
? $this->parseDate($quote_data['payment.date'])
: date('y-m-d'),
'transaction_reference' => $this->getString(
$quote_data,
@ -158,10 +152,7 @@ class QuoteTransformer extends BaseTransformer
$transformed['payments'] = [
[
'date' => isset($quote_data['payment.date'])
? date(
'Y-m-d',
strtotime($quote_data['payment.date'])
)
? $this->parseDate($quote_data['payment.date'])
: date('y-m-d'),
'transaction_reference' => $this->getString(
$quote_data,
@ -181,10 +172,7 @@ class QuoteTransformer extends BaseTransformer
$transformed['payments'] = [
[
'date' => isset($quote_data['payment.date'])
? date(
'Y-m-d',
strtotime($quote_data['payment.date'])
)
? $this->parseDate($quote_data['payment.date'])
: date('y-m-d'),
'transaction_reference' => $this->getString(
$quote_data,

View File

@ -65,9 +65,7 @@ class RecurringInvoiceTransformer extends BaseTransformer
'next_send_date_client' => isset($invoice_data['invoice.next_send_date'])
? $this->parseDate($invoice_data['invoice.next_send_date'])
: now()->format('Y-m-d'),
'due_date' => isset($invoice_data['invoice.due_date'])
? $this->parseDate($invoice_data['invoice.due_date'])
: null,
'due_date' => isset($invoice_data['invoice.due_date']) ? $this->parseDate($invoice_data['invoice.due_date']) : null,
'terms' => $this->getString($invoice_data, 'invoice.terms'),
'due_date_days' => 'terms',
'public_notes' => $this->getString(
@ -101,11 +99,8 @@ class RecurringInvoiceTransformer extends BaseTransformer
'invoice.custom_value4'
),
'footer' => $this->getString($invoice_data, 'invoice.footer'),
'partial' => $this->getFloat($invoice_data, 'invoice.partial') > 0 ?: null,
'partial_due_date' => $this->getString(
$invoice_data,
'invoice.partial_due_date'
),
'partial' => $this->getFloat($invoice_data, 'invoice.partial') > 0 ? $this->getFloat($invoice_data, 'invoice.partial') : null,
'partial_due_date' => isset($invoice_data['invoice.partial_due_date']) ? $this->parseDate($invoice_data['invoice.partial_due_date']) : null,
'custom_surcharge1' => $this->getString(
$invoice_data,
'invoice.custom_surcharge1'

View File

@ -49,7 +49,7 @@ class TaskTransformer extends BaseTransformer
'client_id' => $clientId,
'project_id' => $this->getProjectId($projectId, $clientId),
'description' => $this->getString($task_data, 'task.description'),
'status' => $this->getTaskStatusId($task_data),
'status_id' => $this->getTaskStatusId($task_data),
'custom_value1' => $this->getString($task_data, 'task.custom_value1'),
'custom_value2' => $this->getString($task_data, 'task.custom_value2'),
'custom_value3' => $this->getString($task_data, 'task.custom_value3'),
@ -84,7 +84,7 @@ class TaskTransformer extends BaseTransformer
} elseif(isset($item['task.billable']) && is_bool($item['task.billable'])) {
$is_billable = $item['task.billable'];
} else {
$is_billable = false;
$is_billable = true;
}
if(isset($item['task.start_date']) &&

View File

@ -42,8 +42,7 @@ class InvoiceTransformer extends BaseTransformer
'company_id' => $this->company->id,
'client_id' => $this->getClient($this->getString($invoice_data, 'Client Name'), null),
'number' => $this->getString($invoice_data, 'Invoice #'),
'date' => isset($invoice_data['Date Issued']) ? date('Y-m-d', strtotime($invoice_data['Date Issued'])) : null,
// 'currency_id' => $this->getCurrencyByCode( $invoice_data, 'Currency' ),
'date' => isset($invoice_data['Date Issued']) ? $this->parseDate($invoice_data['Date Issued']) : null,
'amount' => 0,
'status_id' => $invoiceStatusMap[$status =
strtolower($this->getString($invoice_data, 'Invoice Status'))] ?? Invoice::STATUS_SENT,
@ -70,7 +69,7 @@ class InvoiceTransformer extends BaseTransformer
if (! empty($invoice_data['Date Paid'])) {
$transformed['payments'] = [[
'date' => date('Y-m-d', strtotime($invoice_data['Date Paid'])),
'date' => $this->parseDate($invoice_data['Date Paid']),
'amount' => $transformed['amount'],
]];
}

View File

@ -45,7 +45,7 @@ class InvoiceTransformer extends BaseTransformer
'company_id' => $this->company->id,
'number' => $this->getString($invoice_data, 'DocumentNumber'),
'notes' => $this->getString($invoice_data, 'Comment'),
'date' => isset($invoice_data['DocumentDate']) ? date('Y-m-d', strtotime($invoice_data['DocumentDate'])) : null,
'date' => isset($invoice_data['DocumentDate']) ? $this->parseDate($invoice_data['DocumentDate']) : null,
// 'currency_id' => $this->getCurrencyByCode( $invoice_data, 'Currency' ),
'amount' => $this->getFloat($invoice_data, 'TotalAmount'),
'status_id' => $invoiceStatusMap[$status =
@ -92,7 +92,7 @@ class InvoiceTransformer extends BaseTransformer
if (! empty($invoice_data['Date Paid'])) {
$transformed['payments'] = [
[
'date' => date('Y-m-d', strtotime($invoice_data['DatePaid'])),
'date' => $this->parseDate($invoice_data['DatePaid']),
'amount' => $this->getFloat($invoice_data, 'Payments'),
],
];

View File

@ -35,8 +35,8 @@ class InvoiceTransformer extends BaseTransformer
'company_id' => $this->company->id,
'client_id' => $this->getClient($this->getString($data, 'Client'), null),
'number' => $this->getString($data, 'Details'),
'date' => isset($data['Date']) ? date('Y-m-d', strtotime($data['Date'])) : null,
'due_date' => isset($data['Due']) ? date('Y-m-d', strtotime($data['Due'])) : null,
'date' => isset($data['Date']) ? $this->parseDate($data['Date']) : null,
'due_date' => isset($data['Due']) ? $this->parseDate($data['Due']) : null,
'status_id' => Invoice::STATUS_SENT,
'line_items' => [
[

View File

@ -43,7 +43,7 @@ class ExpenseTransformer extends BaseTransformer
'vendor_id' => $this->getVendorIdOrCreate($this->getString($data, 'Vendor')),
'number' => $this->getString($data, 'Bill Number'),
'public_notes' => $this->getString($data, 'Notes / Memo'),
'date' => date('Y-m-d', strtotime($data['Transaction Date Added'])) ?: now()->format('Y-m-d'), //27-01-2022
'date' => $this->parseDate($data['Transaction Date Added']) ?: now()->format('Y-m-d'), //27-01-2022
'currency_id' => $this->company->settings->currency_id,
'category_id' => $this->getOrCreateExpenseCategry($data['Account Name']),
'amount' => $amount,

View File

@ -49,10 +49,10 @@ class InvoiceTransformer extends BaseTransformer
'company_id' => $this->company->id,
'client_id' => $this->getClient($customer_name = $this->getString($invoice_data, $customer_key), null),
'number' => $invoice_number = $this->getString($invoice_data, 'Invoice Number'),
'date' => date('Y-m-d', strtotime($invoice_data[$date_key])) ?: now()->format('Y-m-d'), //27-01-2022
'date' => $this->parseDate($invoice_data[$date_key]) ?: now()->format('Y-m-d'), //27-01-2022
'currency_id' => $this->getCurrencyByCode($invoice_data, 'Currency'),
'status_id' => Invoice::STATUS_SENT,
'due_date' => array_key_exists('Due Date', $invoice_data) ? date('Y-m-d', strtotime($invoice_data['Due Date'])) : null,
'due_date' => array_key_exists('Due Date', $invoice_data) ? $this->parseDate($invoice_data['Due Date']) : null,
];
$line_items = [];
@ -81,7 +81,7 @@ class InvoiceTransformer extends BaseTransformer
} elseif (array_key_exists('Account Type', $record) && $record['Account Type'] === 'System Receivable Invoice') {
// This is a payment
$payments[] = [
'date' => date('Y-m-d', strtotime($invoice_data[$date_key])),
'date' => $this->parseDate($invoice_data[$date_key]),
'amount' => $this->getFloat($record, 'Amount (One column)'),
];
} else {
@ -103,7 +103,7 @@ class InvoiceTransformer extends BaseTransformer
if (array_key_exists('Invoice Paid', $record) && $record['Invoice Paid'] > 0) {
$payments[] = [
'date' => date('Y-m-d', strtotime($record['Last Payment Date'])),
'date' => $this->parseDate($record['Last Payment Date']),
'amount' => $this->getFloat($record, 'Invoice Paid'),
];
}

View File

@ -43,8 +43,8 @@ class InvoiceTransformer extends BaseTransformer
// 'client_id' => $this->getClient($this->getString($invoice_data, 'Customer ID'), $this->getString($invoice_data, 'Primary Contact EmailID')),
'client_id' => $this->harvestClient($invoice_data),
'number' => $this->getString($invoice_data, 'Invoice Number'),
'date' => isset($invoice_data['Invoice Date']) ? date('Y-m-d', strtotime($invoice_data['Invoice Date'])) : null,
'due_date' => isset($invoice_data['Due Date']) ? date('Y-m-d', strtotime($invoice_data['Due Date'])) : null,
'date' => isset($invoice_data['Invoice Date']) ? $this->parseDate($invoice_data['Invoice Date']) : null,
'due_date' => isset($invoice_data['Due Date']) ? $this->parseDate($invoice_data['Due Date']) : null,
'po_number' => $this->getString($invoice_data, 'PurchaseOrder'),
'public_notes' => $this->getString($invoice_data, 'Notes'),
'currency_id' => $this->getCurrencyByCode($invoice_data, 'Currency'),
@ -74,7 +74,7 @@ class InvoiceTransformer extends BaseTransformer
if ($transformed['balance'] < $transformed['amount']) {
$transformed['payments'] = [[
'date' => isset($invoice_data['Last Payment Date']) ? date('Y-m-d', strtotime($invoice_data['Invoice Date'])) : date('Y-m-d'),
'date' => isset($invoice_data['Last Payment Date']) ? $this->parseDate($invoice_data['Invoice Date']) : date('Y-m-d'),
'amount' => $transformed['amount'] - $transformed['balance'],
]];
}

View File

@ -215,6 +215,14 @@ class CompanyImport implements ShouldQueue
"convert_rate_to_client",
];
private array $protected_input = [
'client_portal_privacy_policy',
'client_portal_terms',
'portal_custom_footer',
'portal_custom_css',
'portal_custom_head'
];
private array $version_keys = [
'baseline' => [],
'5.7.35' => [
@ -229,6 +237,11 @@ class CompanyImport implements ShouldQueue
'is_template',
]
],
'5.8.51' => [
CompanyGateway::class => [
'always_show_required_fields',
]
]
];
/**
@ -470,9 +483,16 @@ class CompanyImport implements ShouldQueue
$settings->payment_number_counter = 1;
$settings->project_number_counter = 1;
$settings->purchase_order_number_counter = 1;
$this->company->settings = $co->settings;
$this->company->saveSettings($co->settings, $this->company);
$settings->email_style_custom = str_replace(['{!!','!!}','{{','}}','@dd', '@dump', '@if', '@if(','@endif','@isset','@unless','@auth','@empty','@guest','@env','@section','@switch', '@foreach', '@while', '@include', '@each', '@once', '@push', '@use', '@forelse', '@verbatim', '<?php', '@php', '@for','@class','</s','<s','html;base64'], '', $settings->email_style_custom);
$settings->company_logo = (strlen($settings->company_logo) > 2 && stripos($settings->company_logo, 'http') !== false) ? $settings->company_logo : "https://{$settings->company_logo}";
foreach($this->protected_input as $protected_var)
{
$settings->{$protected_var} = str_replace("script", "", $settings->{$protected_var});
}
$this->company->saveSettings($settings, $this->company);
$this->company->save();

View File

@ -11,13 +11,16 @@
namespace App\Jobs\Credit;
use App\Utils\Number;
use App\Models\Credit;
use App\Models\Payment;
use Illuminate\Bus\Queueable;
use Illuminate\Support\Carbon;
use App\DataMapper\InvoiceItem;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ApplyCreditPayment implements ShouldQueue
{
@ -67,6 +70,19 @@ class ApplyCreditPayment implements ShouldQueue
$credit_balance = $this->credit->balance;
$item_date = Carbon::parse($this->payment->date)->format($this->payment->client->date_format());
$invoice_numbers = $this->payment->invoices->pluck('number')->implode(",");
$item = new InvoiceItem();
$item->quantity = 0;
$item->cost = $this->amount * -1;
$item->notes = "{$item_date} - " . ctrans('texts.credit_payment', ['invoice_number' => $invoice_numbers]) . " ". Number::formatMoney($this->amount, $this->payment->client);
$item->type_id = "1";
$line_items = $this->credit->line_items;
$line_items[] = $item;
$this->credit->line_items = $line_items;
if ($this->amount == $credit_balance) { //total credit applied.
$this->credit
->service()

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