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

Merge pull request #9969 from turbo124/v5-develop

v5.10.27
This commit is contained in:
David Bomba 2024-09-02 10:36:27 +10:00 committed by GitHub
commit 0fc7a27cf4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
43 changed files with 395 additions and 243 deletions

View File

@ -1 +1 @@
5.10.26
5.10.27

View File

@ -140,7 +140,7 @@ class EmailTemplateDefaults
public static function emailPaymentFailedTemplate()
{
return '<p>$client<br><br>'.ctrans('texts.client_payment_failure_body', ['invoice' => '$number', 'amount' => '$amount']).'</p><div class="center">$gateway_payment_error</div><br><div class="center">$payment_button</div>';
return '<p>$client<br><br>'.ctrans('texts.client_payment_failure_body', ['invoice' => '$number', 'amount' => '$amount']).'</p><div>$payment_error</div><br><div>$payment_button</div>';
}
public static function emailQuoteReminder1Subject()
@ -151,7 +151,7 @@ class EmailTemplateDefaults
public static function emailQuoteReminder1Body()
{
return '<p>$client<br><br>'.self::transformText('quote_reminder_message').'</p><div class="center">$view_button</div>';
return '<p>$client<br><br>'.self::transformText('quote_reminder_message').'</p><div>$view_button</div>';
}
@ -177,14 +177,14 @@ class EmailTemplateDefaults
public static function emailInvoiceTemplate()
{
$invoice_message = '<p>$client<br><br>'.self::transformText('invoice_message').'</p><div class="center">$view_button</div>';
$invoice_message = '<p>$client<br><br>'.self::transformText('invoice_message').'</p><div>$view_button</div>';
return $invoice_message;
}
public static function emailInvoiceReminderTemplate()
{
$invoice_message = '<p>$client<br><br>'.self::transformText('reminder_message').'</p><div class="center">$view_button</div>';
$invoice_message = '<p>$client<br><br>'.self::transformText('reminder_message').'</p><div>$view_button</div>';
return $invoice_message;
}
@ -196,7 +196,7 @@ class EmailTemplateDefaults
public static function emailQuoteTemplate()
{
$quote_message = '<p>$client<br><br>'.self::transformText('quote_message').'</p><div class="center">$view_button</div>';
$quote_message = '<p>$client<br><br>'.self::transformText('quote_message').'</p><div>$view_button</div>';
return $quote_message;
}
@ -213,28 +213,28 @@ class EmailTemplateDefaults
public static function emailPurchaseOrderTemplate()
{
$purchase_order_message = '<p>$vendor<br><br>'.self::transformText('purchase_order_message').'</p><div class="center">$view_button</div>';
$purchase_order_message = '<p>$vendor<br><br>'.self::transformText('purchase_order_message').'</p><div>$view_button</div>';
return $purchase_order_message;
}
public static function emailPaymentTemplate()
{
$payment_message = '<p>$client<br><br>'.self::transformText('payment_message').'<br><br>$invoices</p><div class="center">$view_button</div>';
$payment_message = '<p>$client<br><br>'.self::transformText('payment_message').'<br><br>$invoices</p><div>$view_button</div>';
return $payment_message;
}
public static function emailCreditTemplate()
{
$credit_message = '<p>$client<br><br>'.self::transformText('credit_message').'</p><div class="center">$view_button</div>';
$credit_message = '<p>$client<br><br>'.self::transformText('credit_message').'</p><div>$view_button</div>';
return $credit_message;
}
public static function emailPaymentPartialTemplate()
{
$payment_message = '<p>$client<br><br>'.self::transformText('payment_message').'<br><br>$invoices</p><div class="center">$view_button</div>';
$payment_message = '<p>$client<br><br>'.self::transformText('payment_message').'<br><br>$invoices</p><div>$view_button</div>';
return $payment_message;
}

View File

@ -266,58 +266,6 @@ class InvoiceFilters extends QueryFilters
return $this->builder->where('due_date', '>=', $date);
}
/**
* Filter by date range
*
* @param string $date_range
* @return Builder
*/
public function date_range(string $date_range = ''): Builder
{
$parts = explode(",", $date_range);
if (count($parts) != 2) {
return $this->builder;
}
try {
$start_date = Carbon::parse($parts[0]);
$end_date = Carbon::parse($parts[1]);
return $this->builder->whereBetween('date', [$start_date, $end_date]);
} catch(\Exception $e) {
return $this->builder;
}
}
/**
* Filter by due date range
*
* @param string $date_range
* @return Builder
*/
public function due_date_range(string $date_range = ''): Builder
{
$parts = explode(",", $date_range);
if (count($parts) != 2) {
return $this->builder;
}
try {
$start_date = Carbon::parse($parts[0]);
$end_date = Carbon::parse($parts[1]);
return $this->builder->whereBetween('due_date', [$start_date, $end_date]);
} catch(\Exception $e) {
return $this->builder;
}
}
/**
* Sorts the list based on $sort.
*

View File

@ -331,4 +331,61 @@ abstract class QueryFilters
->orderByRaw("{$this->with_property} = ? DESC", [$value])
->company();
}
/**
* Filter by date range
*
* @param string $date_range
* @return Builder
*/
public function date_range(string $date_range = ''): Builder
{
$parts = explode(",", $date_range);
if (count($parts) != 2 || !in_array('date', \Illuminate\Support\Facades\Schema::getColumnListing($this->builder->getModel()->getTable()))) {
return $this->builder;
}
try {
$start_date = Carbon::parse($parts[0]);
$end_date = Carbon::parse($parts[1]);
return $this->builder->whereBetween('date', [$start_date, $end_date]);
} catch(\Exception $e) {
return $this->builder;
}
}
/**
* Filter by due date range
*
* @param string $date_range
* @return Builder
*/
public function due_date_range(string $date_range = ''): Builder
{
$parts = explode(",", $date_range);
if (count($parts) != 2 || !in_array('due_date', \Illuminate\Support\Facades\Schema::getColumnListing($this->builder->getModel()->getTable()))) {
return $this->builder;
}
try {
$start_date = Carbon::parse($parts[0]);
$end_date = Carbon::parse($parts[1]);
return $this->builder->whereBetween('due_date', [$start_date, $end_date]);
} catch(\Exception $e) {
return $this->builder;
}
}
}

View File

@ -23,12 +23,12 @@ class ProRata
* the time interval and the frequency duration
*
* @param float $amount
* @param Carbon $from_date
* @param Carbon $to_date
* @param \Illuminate\Support\Carbon | \Carbon\Carbon $from_date
* @param \Illuminate\Support\Carbon | \Carbon\Carbon $to_date
* @param int $frequency
* @return float
*/
public function refund(float $amount, Carbon $from_date, Carbon $to_date, int $frequency): float
public function refund(float $amount, $from_date, $to_date, int $frequency): float
{
$days = intval(abs($from_date->copy()->diffInDays($to_date)));
$days_in_frequency = $this->getDaysInFrequency($frequency);
@ -41,12 +41,12 @@ class ProRata
* the time interval and the frequency duration
*
* @param float $amount
* @param Carbon $from_date
* @param Carbon $to_date
* @param \Illuminate\Support\Carbon | \Carbon\Carbon $from_date
* @param \Illuminate\Support\Carbon | \Carbon\Carbon $to_date
* @param int $frequency
* @return float
*/
public function charge(float $amount, Carbon $from_date, Carbon $to_date, int $frequency): float
public function charge(float $amount, $from_date, $to_date, int $frequency): float
{
$days = intval(abs($from_date->copy()->diffInDays($to_date)));
$days_in_frequency = $this->getDaysInFrequency($frequency);

View File

@ -13,13 +13,11 @@ namespace App\Http\Requests\Client;
use App\DataMapper\ClientSettings;
use App\Http\Requests\Request;
use App\Http\ValidationRules\Client\CountryCodeExistsRule;
use App\Http\ValidationRules\Ninja\CanStoreClientsRule;
use App\Http\ValidationRules\ValidClientGroupSettingsRule;
use App\Models\Client;
use App\Models\GroupSetting;
use App\Utils\Traits\MakesHash;
use Illuminate\Support\Facades\Cache;
use Illuminate\Validation\Rule;
class StoreClientRequest extends Request

View File

@ -50,7 +50,7 @@ class StoreCompanyRequest extends Request
$rules['portal_domain'] = 'sometimes|url';
} else {
if (Ninja::isHosted()) {
$rules['subdomain'] = ['nullable', 'regex:/^[a-zA-Z0-9][a-zA-Z0-9.-]+[a-zA-Z0-9]$/', new ValidSubdomain()];
$rules['subdomain'] = ['nullable', 'regex:/^[a-zA-Z0-9-]{1,63}$/', new ValidSubdomain()];
} else {
$rules['subdomain'] = 'nullable|alpha_num';
}

View File

@ -1,59 +0,0 @@
<?php
/**
* Credit Ninja (https://creditninja.com).
*
* @link https://github.com/creditninja/creditninja source repository
*
* @copyright Copyright (c) 2022. Credit Ninja LLC (https://creditninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\ValidationRules\Client;
use App\Models\Country;
use Illuminate\Contracts\Validation\Rule;
/**
* Class UniqueCreditNumberRule.
*/
class CountryCodeExistsRule implements Rule
{
public function __construct()
{
}
/**
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
return $this->checkIfCodeExists($value); //if it exists, return false!
}
/**
* @return string
*/
public function message()
{
return 'Country code does not exist';
}
/**
* @return bool
*/
private function checkIfCodeExists($value): bool
{
$country = Country::where('iso_3166_2', $value)
->orWhere('iso_3166_3', $value)
->exists();
if ($country) {
return true;
}
return false;
}
}

View File

@ -12,36 +12,26 @@
namespace App\Http\ValidationRules\Company;
use App\Utils\Ninja;
use Illuminate\Contracts\Validation\Rule;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
/**
* Class ValidCompanyQuantity.
*/
class ValidCompanyQuantity implements Rule
class ValidCompanyQuantity implements ValidationRule
{
/**
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (config('ninja.testvars.travis')) {
return true;
}
$message = ctrans('texts.company_limit_reached', ['limit' => Ninja::isSelfHost() ? 10 : auth()->user()->company()->account->hosted_company_count]);
if (Ninja::isSelfHost()) {
return auth()->user()->company()->account->companies->count() < 10;
}
$test = Ninja::isSelfHost() ?
auth()->user()->company()->account->companies->count() < 10 :
(auth()->user()->account->isPaid() || auth()->user()->account->isTrial()) && auth()->user()->company()->account->companies->count() < 10 ;
return (auth()->user()->account->isPaid() || auth()->user()->account->isTrial()) && auth()->user()->company()->account->companies->count() < 10 ;
if (!$test) {
$fail($message);
}
}
/**
* @return string
*/
public function message()
{
return ctrans('texts.company_limit_reached', ['limit' => Ninja::isSelfHost() ? 10 : auth()->user()->company()->account->hosted_company_count]);
}
}

View File

@ -12,31 +12,22 @@
namespace App\Http\ValidationRules\Company;
use App\Libraries\MultiDB;
use Illuminate\Contracts\Validation\Rule;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
/**
* Class ValidCompanyQuantity.
* Class ValidSubdomain.
*/
class ValidSubdomain implements Rule
class ValidSubdomain implements ValidationRule
{
public function __construct()
public function validate(string $attribute, mixed $value, Closure $fail): void
{
}
public function passes($attribute, $value)
{
if (empty($value)) {
return true;
if(empty($value))
return;
if (!MultiDB::checkDomainAvailable($value)) {
$fail(ctrans('texts.subdomain_taken'));
}
return MultiDB::checkDomainAvailable($value);
}
/**
* @return string
*/
public function message()
{
return ctrans('texts.subdomain_taken');
}
}

View File

@ -165,6 +165,7 @@ class InvoicePay extends Component
return $this->required_fields = true;
}
/** @var \App\Models\ClientContact $contact */
$contact = $this->getContext()['contact'];
foreach ($fields as $index => $field) {
@ -173,7 +174,7 @@ class InvoicePay extends Component
if (\Illuminate\Support\Str::startsWith($field['name'], 'client_')) {
if (
empty($contact->client->{$_field})
|| is_null($contact->client->{$_field})
|| is_null($contact->client->{$_field}) //@phpstan-ignore-line
) {
return $this->required_fields = true;
@ -182,7 +183,7 @@ class InvoicePay extends Component
}
if (\Illuminate\Support\Str::startsWith($field['name'], 'contact_')) {
if (empty($contact->{$_field}) || is_null($contact->{$_field}) || str_contains($contact->{$_field}, '@example.com')) {
if (empty($contact->{$_field}) || is_null($contact->{$_field}) || str_contains($contact->{$_field}, '@example.com')) { //@phpstan-ignore-line
return $this->required_fields = true;
}
}

View File

@ -123,7 +123,7 @@ class ClientPaymentFailureObject
$signature = $this->client->getSetting('email_signature');
$html_variables = (new HtmlEngine($invitation))->makeValues();
$html_variables['$gateway_payment_error'] = $this->error ?? '';
$html_variables['$payment_error'] = $this->error ?? '';
$html_variables['$total'] = $this->getAmount();
$signature = str_replace(array_keys($html_variables), array_values($html_variables), $signature);

View File

@ -298,12 +298,37 @@ class BaseModel extends Model
}
// special catch here for einvoicing eventing
if($event_id == Webhook::EVENT_SENT_INVOICE && ($this instanceof Invoice) && is_null($this->backup)){
if($event_id == Webhook::EVENT_SENT_INVOICE && ($this instanceof Invoice) && is_null($this->backup) && $this->client->getSetting('e_invoice_type') == 'PEPPOL'){
\App\Services\EDocument\Jobs\SendEDocument::dispatch(get_class($this), $this->id, $this->company->db);
}
}
/**
* arrayFilterRecursive
*
* Removes null properties from an array
*
* @param array $array
* @return array
*/
public function arrayFilterRecursive(array $array): array
{
foreach ($array as $key => $value) {
if (is_array($value)) {
// Recursively filter the nested array
$array[$key] = $this->arrayFilterRecursive($value);
}
// Remove null values
if (is_null($array[$key])) {
unset($array[$key]);
}
}
return $array;
}
/**
* Returns the base64 encoded PDF string of the entity
* @deprecated - unused implementation

View File

@ -50,7 +50,7 @@ use Laracasts\Presenter\PresentableTrait;
* @property int|null $last_login
* @property int|null $industry_id
* @property int|null $size_id
* @property object|null $e_invoice
* @property object|array|null $e_invoice
* @property string|null $address1
* @property string|null $address2
* @property string|null $city

View File

@ -37,6 +37,7 @@ use Laracasts\Presenter\PresentableTrait;
* @property-read Project|null $project
* @property-read int|null $tasks_count
* @property-read \App\Models\User $user
* @property-read \App\Models\User $assigned_user
* @property-read \App\Models\Vendor|null $vendor
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel company()
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns)
@ -117,6 +118,11 @@ class Project extends BaseModel
return $this->belongsTo(User::class)->withTrashed();
}
public function assigned_user(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(User::class, 'assigned_user_id', 'id')->withTrashed();
}
public function tasks(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(Task::class);

View File

@ -400,8 +400,12 @@ class BaseDriver extends AbstractPaymentDriver
$invoice = $this->payment_hash->fee_invoice;
$fee_count = collect($invoice->line_items)
->map(function ($item){
$item->gross_line_total = round($item->gross_line_total, 2);
return $item;
})
->whereIn('type_id', ['3','4'])
->where('gross_line_total', $fee_total)
->where('gross_line_total', round($fee_total,2))
->count();
if($invoice && $fee_count == 0){
@ -425,15 +429,15 @@ class BaseDriver extends AbstractPaymentDriver
$invoice_items = (array) $invoice->line_items;
$invoice_items[] = $invoice_item;
if (isset($data['gateway_type_id']) && $fees_and_limits = $this->company_gateway->getFeesAndLimits($data['gateway_type_id'])) {
$invoice_item->tax_rate1 = $fees_and_limits->fee_tax_rate1;
$invoice_item->tax_name1 = $fees_and_limits->fee_tax_name1;
$invoice_item->tax_rate2 = $fees_and_limits->fee_tax_rate2;
$invoice_item->tax_name2 = $fees_and_limits->fee_tax_name2;
$invoice_item->tax_rate3 = $fees_and_limits->fee_tax_rate3;
$invoice_item->tax_name3 = $fees_and_limits->fee_tax_name3;
$invoice_item->tax_id = (string)\App\Models\Product::PRODUCT_TYPE_OVERRIDE_TAX;
}
if (isset($data['gateway_type_id']) && $fees_and_limits = $this->company_gateway->getFeesAndLimits($data['gateway_type_id'])) {
$invoice_item->tax_rate1 = $fees_and_limits->fee_tax_rate1;
$invoice_item->tax_name1 = $fees_and_limits->fee_tax_name1;
$invoice_item->tax_rate2 = $fees_and_limits->fee_tax_rate2;
$invoice_item->tax_name2 = $fees_and_limits->fee_tax_name2;
$invoice_item->tax_rate3 = $fees_and_limits->fee_tax_rate3;
$invoice_item->tax_name3 = $fees_and_limits->fee_tax_name3;
$invoice_item->tax_id = (string)\App\Models\Product::PRODUCT_TYPE_OVERRIDE_TAX;
}
$invoice->line_items = $invoice_items;

View File

@ -105,6 +105,9 @@ class InstantBankPay implements MethodInterface
$this->go_cardless->payment_hash->data->billing_request
);
nlog($billing_request);
$payment = $this->go_cardless->gateway->payments()->get(
$billing_request->payment_request->links->payment
);

View File

@ -286,7 +286,8 @@ class MolliePaymentDriver extends BaseDriver
public function processWebhookRequest(PaymentWebhookRequest $request)
{
// Allow app to catch up with webhook request.
sleep(4);
// sleep(4);
usleep(rand(2800000, 4000000));
$validator = Validator::make($request->all(), [
'id' => ['required', 'starts_with:tr'],

View File

@ -125,6 +125,13 @@ class ImportCustomers
$settings->currency_id = (string) $currency->id;
$client->settings = $settings;
}
}else {
$settings = $client->settings;
$settings->currency_id = (string) $this->stripe->company_gateway->company->settings->currency_id;
$client->settings = $settings;
}
$client->name = $customer->name ? $customer->name : $customer->email;

View File

@ -147,8 +147,7 @@ class BaseRepository
* @throws \ReflectionException
*/
protected function alternativeSave($data, $model)
{ //$start = microtime(true);
//forces the client_id if it doesn't exist
{
if (array_key_exists('client_id', $data)) {
$model->client_id = $data['client_id'];
}
@ -167,7 +166,7 @@ class BaseRepository
$company_defaults = $client->setCompanyDefaults($data, lcfirst($resource));
$data['exchange_rate'] = $company_defaults['exchange_rate'];
$model->uses_inclusive_taxes = $client->getSetting('inclusive_taxes');
// $data = array_merge($company_defaults, $data);
$data = array_merge($data, $company_defaults);
}
@ -321,6 +320,17 @@ class BaseRepository
UpdateTaxData::dispatch($client, $client->company);
}
/** If Peppol is enabled - we will save the e_invoice document here at this point, document will not update after being sent */
if(strlen($model->backup ?? '') == 0 && $client->getSetting('e_invoice_type') == 'PEPPOL' && $model->company->legal_entity_id)
{
try{
$model->service()->getEInvoice();
}
catch(\Exception $e){
nlog("EXCEPTION:: BASEREPOSITORY:: Error generating e_invoice for model {$model->id}");
nlog($e->getMessage());
}
}
}
if ($model instanceof Credit) {

View File

@ -76,6 +76,13 @@ class ClientRepository extends BaseRepository
$client->country_id = $company->settings->country_id;
}
if(isset($data['e_invoice']) && is_array($data['e_invoice'])) {
//ensure it is normalized first!
$data['e_invoice'] = $client->arrayFilterRecursive($data['e_invoice']);
$client->e_invoice = $data['e_invoice'];
}
$client->save();
if (! isset($client->number) || empty($client->number) || strlen($client->number) == 0) {
@ -106,6 +113,7 @@ class ClientRepository extends BaseRepository
if (array_key_exists('contacts', $contact_data) || $client->contacts()->count() == 0) {
$this->contact_repo->save($contact_data, $client);
}
return $client;
}

View File

@ -60,7 +60,7 @@ class CompanyRepository extends BaseRepository
if(isset($data['e_invoice']) && is_array($data['e_invoice'])){
//ensure it is normalized first!
$data['e_invoice'] = $this->arrayFilterRecursive($data['e_invoice']);
$data['e_invoice'] = $company->arrayFilterRecursive($data['e_invoice']);
$company->e_invoice = $data['e_invoice'];
}
@ -70,24 +70,6 @@ class CompanyRepository extends BaseRepository
return $company;
}
private function arrayFilterRecursive(array $array): array
{
foreach ($array as $key => $value) {
if (is_array($value)) {
// Recursively filter the nested array
$array[$key] = $this->arrayFilterRecursive($value);
}
// Remove null values
if (is_null($array[$key])) {
unset($array[$key]);
}
}
return $array;
}
/**
* parseCustomFields
*

View File

@ -69,7 +69,7 @@ class RFFService
if (Str::startsWith($field['name'], 'client_')) {
if (
empty($_contact->client->{$_field})
|| is_null($_contact->client->{$_field})
|| is_null($_contact->client->{$_field}) //@phpstan-ignore-line
) {
// $this->show_form = true;
$this->unfilled_fields++;
@ -79,7 +79,7 @@ class RFFService
}
if (Str::startsWith($field['name'], 'contact_')) {
if (empty($_contact->{$_field}) || is_null($_contact->{$_field}) || str_contains($_contact->{$_field}, '@example.com')) {
if (empty($_contact->{$_field}) || is_null($_contact->{$_field}) || str_contains($_contact->{$_field}, '@example.com')) { //@phpstan-ignore-line
$this->unfilled_fields++;
} else {
$this->fields[$index]['filled'] = true;

View File

@ -261,7 +261,7 @@ class Storecove
*
* @param int $id
* @param array $data
* @return array
* @return mixed
*/
public function updateLegalEntity(int $id, array $data)
{

View File

@ -50,10 +50,11 @@ class SendEDocument implements ShouldQueue
MultiDB::setDB($this->db);
$model = $this->entity::find($this->id);
$e_invoice_standard = $model->client ? $model->client->getSetting('e_invoice_type') : $model->company->getSetting('e_invoice_type');
if($e_invoice_standard != 'PEPPOL')
return;
// $e_invoice_standard = $model->client ? $model->client->getSetting('e_invoice_type') : $model->company->getSetting('e_invoice_type');
// if($e_invoice_standard != 'PEPPOL')
// return;
if(Ninja::isSelfHost() && ($model instanceof Invoice) && $model->company->legal_entity_id)
{
@ -122,10 +123,11 @@ class SendEDocument implements ShouldQueue
$activity->company_id = $model->company_id;
$activity->activity_type_id = Activity::EMAIL_EINVOICE_SUCCESS;
$activity->invoice_id = $model->id;
$activity->notes = $guid;
$activity->notes = str_replace('"', '', $guid);
$activity->save();
$model->backup = $guid;
$model->backup = str_replace('"', '', $guid);
$model->saveQuietly();
}

View File

@ -100,7 +100,7 @@ class OrderXDocument extends AbstractService
$this->orderxdocument->setDocumentShipToAddress($settings_entity->shipping_address1, $settings_entity->shipping_address2, "", $settings_entity->shipping_postal_code, $settings_entity->shipping_city, $settings_entity->shipping_country->iso_3166_2, $settings_entity->shipping_state);
}
$this->orderxdocument->addDocumentPaymentMean(68, ctrans("texts.xinvoice_online_payment"));
$this->orderxdocument->addDocumentPaymentMean('68', ctrans("texts.xinvoice_online_payment"));
if (str_contains($company->getSetting('vat_number'), "/")) {
$this->orderxdocument->addDocumentSellerTaxRegistration("FC", $company->getSetting('vat_number'));

View File

@ -276,7 +276,7 @@ class Peppol extends AbstractService
$this->p_invoice->DueDate = new \DateTime($this->invoice->due_date);
}
$this->p_invoice->InvoiceTypeCode = 380; //
$this->p_invoice->InvoiceTypeCode = ($this->invoice->amount >= 0) ? 380 : 381; //
$this->p_invoice->AccountingSupplierParty = $this->getAccountingSupplierParty();
$this->p_invoice->AccountingCustomerParty = $this->getAccountingCustomerParty();
$this->p_invoice->InvoiceLine = $this->getInvoiceLines();
@ -287,8 +287,11 @@ class Peppol extends AbstractService
$this->senderSpecificLevelMutators()
->receiverSpecificLevelMutators();
$this->invoice->e_invoice = $this->toObject();
$this->invoice->save();
if(strlen($this->invoice->backup ?? '') == 0)
{
$this->invoice->e_invoice = $this->toObject();
$this->invoice->save();
}
return $this;
@ -335,7 +338,7 @@ class Peppol extends AbstractService
/**
* getInvoice
*
* @return InvoiceNinja\EInvoice\Models\Peppol\Invoice
* @return \InvoiceNinja\EInvoice\Models\Peppol\Invoice
*/
public function getInvoice(): \InvoiceNinja\EInvoice\Models\Peppol\Invoice
{

View File

@ -124,9 +124,9 @@ class ZugferdEDokument extends AbstractService
//Payment Means - Switcher
if($company->settings->custom_value1 == '42') {
$this->xdocument->addDocumentPaymentMean(typecode: 42, payeeIban: $company->settings->custom_value2, payeeAccountName: $company->settings->custom_value4, payeeBic: $company->settings->custom_value3);
$this->xdocument->addDocumentPaymentMean(typecode: '42', payeeIban: $company->settings->custom_value2, payeeAccountName: $company->settings->custom_value4, payeeBic: $company->settings->custom_value3);
} else {
$this->xdocument->addDocumentPaymentMean(68, ctrans("texts.xinvoice_online_payment"));
$this->xdocument->addDocumentPaymentMean('68', ctrans("texts.xinvoice_online_payment"));
}
if (str_contains($company->getSetting('vat_number'), "/")) {

View File

@ -92,6 +92,8 @@ class AddGatewayFee extends AbstractService
/**Refresh Invoice values*/
$this->invoice = $this->invoice->calc()->getInvoice();
nlog($this->invoice->line_items);
$new_balance = $this->invoice->balance;
if (floatval($new_balance) - floatval($balance) != 0) {
@ -141,12 +143,6 @@ class AddGatewayFee extends AbstractService
if (floatval($new_balance) - floatval($balance) != 0) {
$adjustment = $new_balance - $balance;
// $this->invoice
// ->client
// ->service()
// ->updateBalance($adjustment * -1)
// ->save();
$this->invoice
->ledger()
->updateInvoiceBalance($adjustment * -1, 'Adjustment for adding gateway DISCOUNT');

View File

@ -148,9 +148,9 @@ class AutoBillInvoice extends AbstractService
]);
nlog("Payment hash created => {$payment_hash->id}");
$this->invoice->saveQuietly();
$payment = false;
try {
$payment = $gateway_token->gateway
->driver($this->client)
@ -163,6 +163,7 @@ class AutoBillInvoice extends AbstractService
}
$this->invoice = $this->invoice->fresh();
$this->invoice->auto_bill_tries += 1;
if ($this->invoice->auto_bill_tries == 3) {

View File

@ -102,9 +102,9 @@ class QuickbooksSync implements ShouldQueue
private function processEntitySync(string $entity, $records)
{
match($entity){
// 'client' => $this->syncQbToNinjaClients($records),
'client' => $this->syncQbToNinjaClients($records),
'product' => $this->syncQbToNinjaProducts($records),
// 'invoice' => $this->syncQbToNinjaInvoices($records),
'invoice' => $this->syncQbToNinjaInvoices($records),
// 'vendor' => $this->syncQbToNinjaClients($records),
// 'quote' => $this->syncInvoices($records),
// 'purchase_order' => $this->syncInvoices($records),

View File

@ -949,6 +949,7 @@ class TemplateService
'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) : [],
'client' => $this->getClient($task),
];
@ -1007,6 +1008,7 @@ class TemplateService
'tasks' => ($project->tasks && !$nested) ? $this->processTasks($project->tasks, true) : [], //@phpstan-ignore-line
'client' => $this->getClient($project),
'user' => $this->userInfo($project->user),
'assigned_user' => $project->assigned_user ? $this->userInfo($project->assigned_user) : [],
'invoices' => $this->processInvoices($project->invoices)
];

View File

@ -401,11 +401,17 @@ class HtmlEngine
$data['$user.name'] = ['value' => $this->entity->user->present()->name(), 'label' => ctrans('texts.name')];
$data['$user.signature'] = ['value' => $this->entity->user->signature ?? '', 'label' => ctrans('texts.signature')];
$data['$user.first_name'] = ['value' => $this->entity->user->first_name, 'label' => ctrans('texts.first_name')];
$data['$user.last_name'] = ['value' => $this->entity->user->last_name, 'label' => ctrans('texts.last_name')];
$data['$created_by_user'] = &$data['$user.name'];
$data['$assigned_to_user'] = ['value' => $this->entity->assigned_user ? $this->entity->assigned_user->present()->name() : '', 'label' => ctrans('texts.name')];
$data['$assigned_user.signature'] = ['value' => $this->entity->assigned_user ? $this->entity->assigned_user->signature : '', 'label' => ctrans('texts.signature')];
$data['$assigned_user.first_name'] = ['value' => $this->entity->assigned_user ? $this->entity->assigned_user->first_name : '', 'label' => ctrans('texts.first_name')];
$data['$assigned_user.last_name'] = ['value' => $this->entity->assigned_user ? $this->entity->assigned_user->last_name : '', 'label' => ctrans('texts.last_name')];
$data['$user_iban'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company1', $this->settings->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company1')];
@ -666,7 +672,7 @@ class HtmlEngine
}
$data['$contact.signature_raw'] = ['value' => $this->invitation->signature_base64, 'label' => ctrans('texts.signature')];
$data['$contact.signature_date'] = ['value' => $this->translateDate($this->invitation->signature_date ?? '1970-01-01', $this->client->date_format(), $this->client->locale()), 'label' => ctrans('texts.date')];
$data['$contact.signature_date'] = ['value' => $this->invitation->signature_date ? $this->translateDate($this->invitation->signature_date, $this->client->date_format(), $this->client->locale()) : ' ', 'label' => ctrans('texts.date')];
$data['$contact.signature_ip'] = ['value' => $this->invitation->signature_ip ?? '', 'label' => ctrans('texts.address')];
$data['$thanks'] = ['value' => '', 'label' => ctrans('texts.thanks')];
@ -733,7 +739,7 @@ class HtmlEngine
$data['$payment.number'] = ['value' => '', 'label' => ctrans('texts.payment_number')];
$data['$payment.transaction_reference'] = ['value' => '', 'label' => ctrans('texts.transaction_reference')];
$data['$payment.refunded'] = ['value' => '', 'label' => ctrans('texts.refund')];
$data['$gateway_payment_error'] = ['value' => '', 'label' => ctrans('texts.error')];
$data['$payment_error'] = ['value' => '', 'label' => ctrans('texts.error')];
if ($this->entity_string == 'invoice' && $this->entity->net_payments()->exists()) {
$payment_list = '<br><br>';
@ -1166,7 +1172,7 @@ class HtmlEngine
}
return '
<div>
<div class=\"center\">
<!--[if (gte mso 9)|(IE)]>
<table align="center" cellspacing="0" cellpadding="0" style="width: 600px;">
<tr>

View File

@ -201,8 +201,8 @@
"url": "https://github.com/turbo124/snappdf"
},
{
"type":"vcs",
"url":"https://github.com/karneaud/QuickBooks-V3-PHP-SDK.git"
"type": "vcs",
"url": "https://github.com/karneaud/QuickBooks-V3-PHP-SDK.git"
}
],
"minimum-stability": "dev",

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

View File

@ -1,5 +1,6 @@
<?php
use App\Models\Gateway;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
@ -25,6 +26,12 @@ return new class extends Migration
$currency->symbol = 'лв';
$currency->save();
}
if($gateway = Gateway::find(15))
{
$gateway->visible = 0;
$gateway->save();
}
}
/**

View File

@ -105,7 +105,7 @@ class PaymentLibrariesSeeder extends Seeder
Gateway::query()->update(['visible' => 0]);
Gateway::whereIn('id', [1, 3, 7, 11, 15, 20, 39, 46, 55, 50, 57, 52, 58, 59, 60, 62, 63])->update(['visible' => 1]);
Gateway::whereIn('id', [1, 3, 7, 11, 20, 39, 46, 55, 50, 57, 52, 58, 59, 60, 62, 63])->update(['visible' => 1]);
if (Ninja::isHosted()) {
Gateway::whereIn('id', [20, 49])->update(['visible' => 0]);

View File

@ -5325,7 +5325,9 @@ $lang = array(
'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',
);
return $lang;

View File

@ -5319,6 +5319,13 @@ E-mail: :email<b><br><b>',
'one_page_checkout' => 'Afrekenen op één pagina',
'one_page_checkout_help' => 'Schakel de nieuwe betaalflow \'afrekenen op één pagina\' in',
'applies_to' => 'Is van toepassing op',
'accept_purchase_order' => 'Accepteer inkooporder',
'round_to_seconds' => 'Afronden op seconden',
'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',
);
return $lang;

View File

@ -67,7 +67,7 @@ use InvoiceNinja\EInvoice\Models\Peppol\TaxCategoryType\TaxCategory;
/**
* @test
*/
class FACT1Test extends TestCase
class Fact1Test extends TestCase
{
use MockAccountData;
use DatabaseTransactions;

View File

@ -0,0 +1,68 @@
<?php
/**
* 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
*/
namespace Tests\Integration\Validation;
use Tests\TestCase;
use App\Models\Company;
use Tests\MockUnitData;
use Illuminate\Support\Facades\Validator;
use App\Http\ValidationRules\Company\ValidCompanyQuantity;
/**
* @test
*/
class ValidCompanyQuantityTest extends TestCase
{
use MockUnitData;
protected function setUp(): void
{
parent::setUp();
$this->makeTestData();
}
/** @test */
public function testCompanyQuantityValidation()
{
auth()->login($this->user, true);
$data =[];
$rules = ['name' => [new ValidCompanyQuantity()]];
$validator = Validator::make($data, $rules);
$this->assertTrue($validator->passes());
}
/** @test */
public function testCompanyQuantityValidationFails()
{
auth()->login($this->user, true);
auth()->user()->setCompany($this->company);
$data =['name' => 'bob'];
$rules = ['name' => [new ValidCompanyQuantity()]];
Company::factory()->count(10)->create([
'account_id' => auth()->user()->account->id,
]);
$validator = Validator::make($data, $rules);
$this->assertFalse($validator->passes());
}
}

View File

@ -0,0 +1,84 @@
<?php
/**
* 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
*/
namespace Tests\Integration\Validation;
use Tests\TestCase;
use App\Models\Company;
use Tests\MockUnitData;
use Illuminate\Support\Facades\Validator;
use App\Http\ValidationRules\Company\ValidCompanyQuantity;
use App\Http\ValidationRules\Company\ValidSubdomain;
/**
* @test
*/
class ValidSubdomainTest extends TestCase
{
use MockUnitData;
protected function setUp(): void
{
parent::setUp();
}
/** @test */
public function testCheckValidSubdomainName()
{
$data = ['subdomain' => 'invoiceyninjay'];
$rules = ['subdomain' => ['nullable', 'regex:/^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/',new ValidSubdomain()]];
$validator = Validator::make($data, $rules);
$this->assertTrue($validator->passes());
}
public function testCheckEmptyValidSubdomainName()
{
$data = ['subdomain' => ''];
$rules = ['subdomain' => ['nullable', 'regex:/^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/',new ValidSubdomain()]];
$validator = Validator::make($data, $rules);
$this->assertTrue($validator->passes());
}
public function testCheckEmpty2ValidSubdomainName()
{
$data = ['subdomain' => ' '];
$rules = ['subdomain' => ['nullable', 'regex:/^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/',new ValidSubdomain()]];
$validator = Validator::make($data, $rules);
$this->assertTrue($validator->passes());
}
/** @test */
public function testCheckInValidSubdomainName()
{
$data = ['subdomain' => 'domain.names'];
$rules = ['subdomain' => ['nullable', 'regex:/^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/',new ValidSubdomain()]];
$validator = Validator::make($data, $rules);
$this->assertFalse($validator->passes());
}
}

View File

@ -9,6 +9,8 @@
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace Tests\Unit;
use App\Jobs\EDocument\CreateEDocument;
use App\Jobs\Entity\CreateRawPdf;
use horstoeko\zugferd\ZugferdDocumentReader;