Merge pull request #11186 from turbo124/v5-develop

PEPPOL adjustments
This commit is contained in:
David Bomba 2025-08-15 18:51:49 +10:00 committed by GitHub
commit d9bafd41b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 482 additions and 210 deletions

View File

@ -1 +1 @@
5.12.14 5.12.15

View File

@ -135,18 +135,18 @@ class ContactExport extends BaseExport
} }
} }
// return $entity;
return $this->decorateAdvancedFields($contact->client, $entity); return $this->decorateAdvancedFields($contact->client, $entity);
} }
private function decorateAdvancedFields(Client $client, array $entity): array private function decorateAdvancedFields(Client $client, array $entity): array
{ {
if (in_array('client.country_id', $this->input['report_keys'])) { if (in_array('client.country_id', $this->input['report_keys'])) {
$entity['country'] = $client->country ? ctrans("texts.country_{$client->country->name}") : ''; $entity['client.country_id'] = $client->country ? ctrans("texts.country_{$client->country->name}") : '';
} }
if (in_array('client.shipping_country_id', $this->input['report_keys'])) { if (in_array('client.shipping_country_id', $this->input['report_keys'])) {
$entity['shipping_country'] = $client->shipping_country ? ctrans("texts.country_{$client->shipping_country->name}") : ''; $entity['client.shipping_country_id'] = $client->shipping_country ? ctrans("texts.country_{$client->shipping_country->name}") : '';
} }
if (in_array('client.currency', $this->input['report_keys'])) { if (in_array('client.currency', $this->input['report_keys'])) {

View File

@ -113,7 +113,6 @@ class LoginController extends BaseController
->increment() ->increment()
->batch(); ->batch();
$ip = ''; $ip = '';
if (request()->hasHeader('Cf-Connecting-Ip')) { if (request()->hasHeader('Cf-Connecting-Ip')) {
@ -330,15 +329,15 @@ class LoginController extends BaseController
Auth::login($existing_login_user, false); Auth::login($existing_login_user, false);
/** @var \App\Models\User $user */ /** @var \App\Models\User $user */
$user = auth()->user(); // $user = auth()->user();
$user->update([ $existing_login_user->update([
'oauth_user_id' => $user->id, 'oauth_user_id' => $user->id,
'oauth_provider_id' => $provider, 'oauth_provider_id' => $provider,
]); ]);
/** @var \App\Models\CompanyUser $cu */ /** @var \App\Models\CompanyUser $cu */
$cu = $this->hydrateCompanyUser($user); $cu = $this->hydrateCompanyUser($existing_login_user);
if ($cu->count() == 0) { if ($cu->count() == 0) {
return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
@ -378,22 +377,20 @@ class LoginController extends BaseController
$account = (new CreateAccount($new_account, request()->getClientIp()))->handle(); $account = (new CreateAccount($new_account, request()->getClientIp()))->handle();
Auth::login($account->default_company->owner(), false); $account_user = $account->default_company->owner();
Auth::login($account_user, false);
/** @var \App\Models\User $user */ $account_user->email_verified_at = now();
$user = auth()->user(); $account_user->save();
$user->email_verified_at = now();
$user->save();
/** @var \App\Models\CompanyUser $cu */ /** @var \App\Models\CompanyUser $cu */
$cu = $this->hydrateCompanyUser($user); $cu = $this->hydrateCompanyUser($account_user);
if ($cu->count() == 0) { if ($cu->count() == 0) {
return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400); return response()->json(['message' => 'User found, but not attached to any companies, please see your administrator'], 400);
} }
if (Ninja::isHosted() && !$cu->first()->is_owner && !auth()->user()->account->isEnterprisePaidClient()) { if (Ninja::isHosted() && !$cu->first()->is_owner && !$account_user->account->isEnterprisePaidClient()) {
return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403); return response()->json(['message' => 'Pro / Free accounts only the owner can log in. Please upgrade'], 403);
} }

View File

@ -39,7 +39,8 @@ class ProtectedDownloadController extends BaseController
return response()->streamDownload(function () use ($hashed_path) { return response()->streamDownload(function () use ($hashed_path) {
$stream = Storage::readStream($hashed_path); $stream = Storage::readStream($hashed_path);
if ($stream === false) { // if($stream ===false){
if ($stream === null) {
throw new SystemError('Unable to read file', 500); throw new SystemError('Unable to read file', 500);
} }

View File

@ -60,6 +60,7 @@ class ConnectNordigenBankIntegrationRequest extends Request
/** /**
* @return array{ * @return array{
* user_id: int, * user_id: int,
* bank_account_id?: string,
* company_key: string, * company_key: string,
* context: string, * context: string,
* is_react: bool, * is_react: bool,

View File

@ -56,11 +56,12 @@ class PaymentScheduleRequest extends Request
$input['is_paused'] = false; $input['is_paused'] = false;
$input['parameters']['auto_bill'] = (bool) isset($input['parameters']['auto_bill']) ? $input['parameters']['auto_bill'] : false; $input['parameters']['auto_bill'] = (bool) isset($input['parameters']['auto_bill']) ? $input['parameters']['auto_bill'] : false;
$input['parameters']['schedule'] = [];
if(isset($input['parameters']['schedule']) && is_array($input['parameters']['schedule']) && count($input['parameters']['schedule']) > 0) { if(isset($input['parameters']['schedule']) && is_array($input['parameters']['schedule']) && count($input['parameters']['schedule']) > 0) {
$input['parameters']['schedule'] = $input['parameters']['schedule']; $input['parameters']['schedule'] = $input['parameters']['schedule'];
} }
else{
$input['parameters']['schedule'] = [];
}
if (isset($input['schedule']) && is_array($input['schedule']) && count($input['schedule']) > 0) { if (isset($input['schedule']) && is_array($input['schedule']) && count($input['schedule']) > 0) {
$schedule_map = collect($input['schedule'])->map(function ($schedule, $key) { $schedule_map = collect($input['schedule'])->map(function ($schedule, $key) {
@ -150,6 +151,7 @@ class PaymentScheduleRequest extends Request
RecurringInvoice::FREQUENCY_ANNUALLY => $date->startOfDay()->addYear(), RecurringInvoice::FREQUENCY_ANNUALLY => $date->startOfDay()->addYear(),
RecurringInvoice::FREQUENCY_TWO_YEARS => $date->startOfDay()->addYears(2), RecurringInvoice::FREQUENCY_TWO_YEARS => $date->startOfDay()->addYears(2),
RecurringInvoice::FREQUENCY_THREE_YEARS => $date->startOfDay()->addYears(3), RecurringInvoice::FREQUENCY_THREE_YEARS => $date->startOfDay()->addYears(3),
default => $date->startOfDay()->addMonthNoOverflow(),
}; };
} }
} }

View File

@ -118,7 +118,7 @@ class InvoiceTransactionEventEntry
return new TransactionEventMetadata([ return new TransactionEventMetadata([
'tax_report' => [ 'tax_report' => [
'tax_details' => $details, 'tax_details' => $details,
'payment_history' => $this->payments->toArray() ?? [], 'payment_history' => $this->payments->toArray() ?? [], //@phpstan-ignore-line
'tax_summary' => [ 'tax_summary' => [
'total_taxes' => $invoice->total_taxes, 'total_taxes' => $invoice->total_taxes,
'total_paid' => $this->getTotalTaxPaid($invoice), 'total_paid' => $this->getTotalTaxPaid($invoice),

View File

@ -75,6 +75,7 @@ use Laracasts\Presenter\PresentableTrait;
* @property bool $is_trial * @property bool $is_trial
* @property int $e_invoice_quota * @property int $e_invoice_quota
* @property int $docuninja_num_users * @property int $docuninja_num_users
* @property string|null $e_invoicing_token
* @property-read int|null $bank_integrations_count * @property-read int|null $bank_integrations_count
* @property-read int|null $companies_count * @property-read int|null $companies_count
* @property-read int|null $company_users_count * @property-read int|null $company_users_count

View File

@ -87,6 +87,9 @@ use Illuminate\Contracts\Translation\HasLocalePreference;
* @property-read \App\Models\User $user * @property-read \App\Models\User $user
* @property-read \App\Models\Company $company * @property-read \App\Models\Company $company
* @property-read \App\Models\Country|null $country * @property-read \App\Models\Country|null $country
* @property-read \App\Models\Industry|null $industry
* @property-read \App\Models\Country|null $shipping_country
* @property-read \App\Models\Size|null $size
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\CompanyLedger> $company_ledger * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\CompanyLedger> $company_ledger
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ClientContact> $contacts * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ClientContact> $contacts
@ -495,7 +498,7 @@ class Client extends BaseModel implements HasLocalePreference
return $item->id == $currency_id; return $item->id == $currency_id;
}); });
}); }) ?? \App\Models\Currency::find($this->getSetting('currency_id'));
} }

View File

@ -123,6 +123,7 @@ use Laracasts\Presenter\PresentableTrait;
* @property string|null $inbound_mailbox_blacklist * @property string|null $inbound_mailbox_blacklist
* @property string|null $e_invoice_certificate_passphrase * @property string|null $e_invoice_certificate_passphrase
* @property string|null $e_invoice_certificate * @property string|null $e_invoice_certificate
* @property object|null $origin_tax_data
* @property int $deleted_at * @property int $deleted_at
* @property string|null $smtp_username * @property string|null $smtp_username
* @property string|null $smtp_password * @property string|null $smtp_password
@ -133,6 +134,9 @@ use Laracasts\Presenter\PresentableTrait;
* @property \App\DataMapper\QuickbooksSettings|null $quickbooks * @property \App\DataMapper\QuickbooksSettings|null $quickbooks
* @property boolean $smtp_verify_peer * @property boolean $smtp_verify_peer
* @property int|null $legal_entity_id * @property int|null $legal_entity_id
* @property bool $invoice_task_item_description
* @property bool $show_task_item_description
* @property bool $invoice_task_project_header
* @property-read \App\Models\Account $account * @property-read \App\Models\Account $account
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read int|null $activities_count * @property-read int|null $activities_count

View File

@ -118,6 +118,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property \App\Models\User $user * @property \App\Models\User $user
* @property \App\Models\Client $client * @property \App\Models\Client $client
* @property \App\Models\Vendor|null $vendor * @property \App\Models\Vendor|null $vendor
* @property-read \App\Models\Location|null $location
* @property-read mixed $pivot * @property-read mixed $pivot
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\CompanyLedger> $company_ledger * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\CompanyLedger> $company_ledger

View File

@ -30,6 +30,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property int|null $created_at * @property int|null $created_at
* @property int|null $updated_at * @property int|null $updated_at
* @property int|null $deleted_at * @property int|null $deleted_at
* @property string|null $entities
* @property-read \App\Models\Company|null $company * @property-read \App\Models\Company|null $company
* @property-read string $hashed_id * @property-read string $hashed_id
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel company() * @method static \Illuminate\Database\Eloquent\Builder|BaseModel company()

View File

@ -66,19 +66,20 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property float $tax_amount3 * @property float $tax_amount3
* @property bool $uses_inclusive_taxes * @property bool $uses_inclusive_taxes
* @property bool $calculate_tax_by_amount * @property bool $calculate_tax_by_amount
* @property-read int|null $documents_count
* @property-read mixed $hashed_id
* @property-read \App\Models\User|null $assigned_user * @property-read \App\Models\User|null $assigned_user
* @property-read \App\Models\ExpenseCategory|null $category * @property-read \App\Models\ExpenseCategory|null $category
* @property-read \App\Models\Client|null $client * @property-read \App\Models\Client|null $client
* @property-read \App\Models\Company $company * @property-read \App\Models\Company $company
* @property-read \App\Models\Currency|null $currency * @property-read \App\Models\Currency|null $currency
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read int|null $documents_count
* @property-read mixed $hashed_id
* @property-read \App\Models\PaymentType|null $payment_type * @property-read \App\Models\PaymentType|null $payment_type
* @property-read \App\Models\Project|null $project * @property-read \App\Models\Project|null $project
* @property-read \App\Models\PurchaseOrder|null $purchase_order * @property-read \App\Models\PurchaseOrder|null $purchase_order
* @property-read \App\Models\User $user * @property-read \App\Models\User $user
* @property-read \App\Models\Vendor|null $vendor * @property-read \App\Models\Vendor|null $vendor
* @property-read \App\Models\Currency|null $invoice_currency
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel company() * @method static \Illuminate\Database\Eloquent\Builder|BaseModel company()
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns) * @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns)
* @method static \Database\Factories\ExpenseFactory factory($count = null, $state = []) * @method static \Database\Factories\ExpenseFactory factory($count = null, $state = [])

View File

@ -121,12 +121,14 @@ use App\Utils\Number;
* @property-read int|null $payments_count * @property-read int|null $payments_count
* @property-read mixed $pivot * @property-read mixed $pivot
* @property-read \App\Models\Project|null $project * @property-read \App\Models\Project|null $project
* @property-read \App\Models\Quote|null $quote
* @property-read \App\Models\RecurringInvoice|null $recurring_invoice * @property-read \App\Models\RecurringInvoice|null $recurring_invoice
* @property-read \App\Models\Subscription|null $subscription * @property-read \App\Models\Subscription|null $subscription
* @property-read \App\Models\Task|null $task * @property-read \App\Models\Task|null $task
* @property-read int|null $tasks_count * @property-read int|null $tasks_count
* @property-read \App\Models\User $user * @property-read \App\Models\User $user
* @property-read \App\Models\Vendor|null $vendor * @property-read \App\Models\Vendor|null $vendor
* @property-read \App\Models\Location|null $location
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\TransactionEvent> $transaction_events * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\TransactionEvent> $transaction_events
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\CompanyLedger> $company_ledger * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\CompanyLedger> $company_ledger

View File

@ -34,6 +34,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
* @property bool $is_deleted * @property bool $is_deleted
* @property string|null $number * @property string|null $number
* @property string $color * @property string $color
* @property int|null $current_hours
* @property-read \App\Models\Client|null $client * @property-read \App\Models\Client|null $client
* @property-read \App\Models\Company $company * @property-read \App\Models\Company $company
* @property-read int|null $documents_count * @property-read int|null $documents_count

View File

@ -89,6 +89,9 @@ use App\Events\PurchaseOrder\PurchaseOrderWasEmailed;
* @property int|null $updated_at * @property int|null $updated_at
* @property int|null $expense_id * @property int|null $expense_id
* @property int|null $currency_id * @property int|null $currency_id
* @property int|null $location_id
* @property int|null $invoice_id
* @property object|null $tax_data
* @property-read int|null $activities_count * @property-read int|null $activities_count
* @property \App\Models\User|null $assigned_user * @property \App\Models\User|null $assigned_user
* @property \App\Models\Client|null $client * @property \App\Models\Client|null $client
@ -101,6 +104,8 @@ use App\Events\PurchaseOrder\PurchaseOrderWasEmailed;
* @property \App\Models\User $user * @property \App\Models\User $user
* @property \App\Models\Vendor $vendor * @property \App\Models\Vendor $vendor
* @property \App\Models\PurchaseOrderInvitation $invitation * @property \App\Models\PurchaseOrderInvitation $invitation
* @property \App\Models\Currency|null $currency
* @property \App\Models\Location|null $location
* @method static \Illuminate\Database\Eloquent\Builder|PurchaseOrder exclude($columns) * @method static \Illuminate\Database\Eloquent\Builder|PurchaseOrder exclude($columns)
* @method static \Database\Factories\PurchaseOrderFactory factory($count = null, $state = []) * @method static \Database\Factories\PurchaseOrderFactory factory($count = null, $state = [])
* @method static \Illuminate\Database\Eloquent\Builder|PurchaseOrder filter(\App\Filters\QueryFilters $filters) * @method static \Illuminate\Database\Eloquent\Builder|PurchaseOrder filter(\App\Filters\QueryFilters $filters)

View File

@ -96,6 +96,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property string|null $reminder3_sent * @property string|null $reminder3_sent
* @property string|null $reminder_last_sent * @property string|null $reminder_last_sent
* @property float $paid_to_date * @property float $paid_to_date
* @property object|null $tax_data
* @property int|null $subscription_id * @property int|null $subscription_id
* @property \App\Models\User|null $assigned_user * @property \App\Models\User|null $assigned_user
* @property \App\Models\Client $client * @property \App\Models\Client $client
@ -111,6 +112,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property-read \App\Models\Project|null $project * @property-read \App\Models\Project|null $project
* @property-read \App\Models\User $user * @property-read \App\Models\User $user
* @property-read \App\Models\Vendor|null $vendor * @property-read \App\Models\Vendor|null $vendor
* @property-read \App\Models\Location|null $location
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Backup> $history * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Backup> $history

View File

@ -109,6 +109,7 @@ use App\Models\Presenters\RecurringInvoicePresenter;
* @property-read \App\Models\Subscription|null $subscription * @property-read \App\Models\Subscription|null $subscription
* @property-read \App\Models\User $user * @property-read \App\Models\User $user
* @property-read \App\Models\Vendor|null $vendor * @property-read \App\Models\Vendor|null $vendor
* @property-read \App\Models\Location|null $location
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns) * @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns)
* @method static \Database\Factories\RecurringInvoiceFactory factory($count = null, $state = []) * @method static \Database\Factories\RecurringInvoiceFactory factory($count = null, $state = [])
* @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice filter(\App\Filters\QueryFilters $filters) * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice filter(\App\Filters\QueryFilters $filters)

View File

@ -56,6 +56,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property float $promo_price * @property float $promo_price
* @property int $registration_required * @property int $registration_required
* @property int $use_inventory_management * @property int $use_inventory_management
* @property string|null $steps
* @property string|null $optional_product_ids * @property string|null $optional_product_ids
* @property string|null $optional_recurring_product_ids * @property string|null $optional_recurring_product_ids
* @property-read \App\Models\Company $company * @property-read \App\Models\Company $company

View File

@ -59,6 +59,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property int|null $language_id * @property int|null $language_id
* @property int|null $last_login * @property int|null $last_login
* @property bool $is_tax_exempt * @property bool $is_tax_exempt
* @property string|null $classification
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
* @property-read int|null $activities_count * @property-read int|null $activities_count
* @property-read \App\Models\Language|null $language * @property-read \App\Models\Language|null $language
@ -73,6 +74,8 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\VendorContact> $primary_contact
* @property-read int|null $primary_contact_count * @property-read int|null $primary_contact_count
* @property-read \App\Models\User $user * @property-read \App\Models\User $user
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Location> $locations
* @property-read int|null $locations_count
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns) * @method static \Illuminate\Database\Eloquent\Builder|BaseModel exclude($columns)
* @method static \Database\Factories\VendorFactory factory($count = null, $state = []) * @method static \Database\Factories\VendorFactory factory($count = null, $state = [])
* @method static \Illuminate\Database\Eloquent\Builder|Vendor filter(\App\Filters\QueryFilters $filters) * @method static \Illuminate\Database\Eloquent\Builder|Vendor filter(\App\Filters\QueryFilters $filters)

View File

@ -94,7 +94,6 @@ class ChartService
$data[$key]['expenses'] = $this->getExpenseChartQuery($start_date, $end_date, $key); $data[$key]['expenses'] = $this->getExpenseChartQuery($start_date, $end_date, $key);
} }
$data[999]['invoices'] = $this->getAggregateInvoiceChartQuery($start_date, $end_date); $data[999]['invoices'] = $this->getAggregateInvoiceChartQuery($start_date, $end_date);
$data[999]['outstanding'] = $this->getAggregateOutstandingChartQuery($start_date, $end_date); $data[999]['outstanding'] = $this->getAggregateOutstandingChartQuery($start_date, $end_date);
$data[999]['payments'] = $this->getAggregatePaymentChartQuery($start_date, $end_date); $data[999]['payments'] = $this->getAggregatePaymentChartQuery($start_date, $end_date);

View File

@ -250,11 +250,11 @@ class SendEDocument implements ShouldQueue
nlog($exception->getMessage()); nlog($exception->getMessage());
} }
config(['queue.failed.driver' => null]); // config(['queue.failed.driver' => null]);
} }
public function middleware() public function middleware()
{ {
return [new WithoutOverlapping($this->entity.$this->id.$this->db)]; return [(new WithoutOverlapping($this->entity.$this->id.$this->db))->releaseAfter(60)->expireAfter(60)];
} }
} }

View File

@ -717,6 +717,10 @@ class Peppol extends AbstractService
$tax_type = 'G'; //Free export item, VAT not charged $tax_type = 'G'; //Free export item, VAT not charged
$reason_code = 'vatex-eu-g'; $reason_code = 'vatex-eu-g';
$reason = 'Export outside the EU'; $reason = 'Export outside the EU';
} elseif($this->invoice->client->country->iso_3166_2 == $this->company->country()->iso_3166_2) {
$tax_type = 'E';
$reason_code = "vatex-eu-o";
$reason = 'Services outside scope of tax';
} else { } else {
$tax_type = 'O'; $tax_type = 'O';
$reason_code = "vatex-eu-o"; $reason_code = "vatex-eu-o";

View File

@ -125,7 +125,7 @@ class TaxPeriodReport extends BaseExport
->map(function ($group) { ->map(function ($group) {
return $group->first(); return $group->first();
})->each(function ($pp){ })->each(function ($pp){
nlog($pp->paymentable->id. " - Paid Updater"); // nlog($pp->paymentable->id. " - Paid Updater");
(new InvoiceTransactionEventEntryCash())->run($pp->paymentable, \Carbon\Carbon::parse($pp->created_at)->startOfMonth()->format('Y-m-d'), \Carbon\Carbon::parse($pp->created_at)->endOfMonth()->format('Y-m-d')); (new InvoiceTransactionEventEntryCash())->run($pp->paymentable, \Carbon\Carbon::parse($pp->created_at)->startOfMonth()->format('Y-m-d'), \Carbon\Carbon::parse($pp->created_at)->endOfMonth()->format('Y-m-d'));
}); });

View File

@ -144,7 +144,7 @@ class InvoiceOutstandingTasksService
EmailStatement::THIS_YEAR => [now()->startOfDay()->firstOfYear()->format('Y-m-d'), now()->startOfDay()->lastOfYear()->format('Y-m-d')], EmailStatement::THIS_YEAR => [now()->startOfDay()->firstOfYear()->format('Y-m-d'), now()->startOfDay()->lastOfYear()->format('Y-m-d')],
EmailStatement::LAST_YEAR => [now()->startOfDay()->subYearNoOverflow()->firstOfYear()->format('Y-m-d'), now()->startOfDay()->subYearNoOverflow()->lastOfYear()->format('Y-m-d')], EmailStatement::LAST_YEAR => [now()->startOfDay()->subYearNoOverflow()->firstOfYear()->format('Y-m-d'), now()->startOfDay()->subYearNoOverflow()->lastOfYear()->format('Y-m-d')],
EmailStatement::ALL_TIME => [ EmailStatement::ALL_TIME => [
Task::query() Task::query() //@phpstan-ignore-line
->where('company_id', $this->scheduler->company_id) ->where('company_id', $this->scheduler->company_id)
->where('is_deleted', 0) ->where('is_deleted', 0)
->selectRaw('MIN(tasks.calculated_start_date) as start_date') ->selectRaw('MIN(tasks.calculated_start_date) as start_date')

View File

@ -31,7 +31,7 @@ class TaxService
$vat_check = (new VatNumberCheck($this->client->vat_number, $client_country_code))->run(); $vat_check = (new VatNumberCheck($this->client->vat_number, $client_country_code))->run();
nlog($vat_check); // nlog($vat_check);
if ($vat_check->isValid()) { if ($vat_check->isValid()) {

View File

@ -22,8 +22,14 @@ class VatNumberCheck
public function run() public function run()
{ {
if(strlen($this->vat_number ?? '') == 0){
$this->response = ['valid' => false, 'error' => 'No VAT number provided'];
return $this;
}
else{
return $this->checkvat_number(); return $this->checkvat_number();
} }
}
private function checkvat_number(): self private function checkvat_number(): self
{ {

View File

@ -17,8 +17,8 @@ return [
'require_https' => env('REQUIRE_HTTPS', true), 'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
'app_version' => env('APP_VERSION', '5.12.14'), 'app_version' => env('APP_VERSION', '5.12.15'),
'app_tag' => env('APP_TAG', '5.12.14'), 'app_tag' => env('APP_TAG', '5.12.15'),
'minimum_client_version' => '5.0.16', 'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1', 'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', false), 'api_secret' => env('API_SECRET', false),

View File

@ -22,7 +22,7 @@ use Tests\TestCase;
*/ */
class PeppolApiTest extends TestCase class PeppolApiTest extends TestCase
{ {
// use DatabaseTransactions; use DatabaseTransactions;
use MockAccountData; use MockAccountData;
protected function setUp(): void protected function setUp(): void

View File

@ -42,7 +42,7 @@ use InvoiceNinja\EInvoice\Models\FatturaPA\FatturaElettronicaHeaderType\FatturaE
class PeppolTest extends TestCase class PeppolTest extends TestCase
{ {
// use DatabaseTransactions; use DatabaseTransactions;
use MockAccountData; use MockAccountData;
protected int $iterations = 10; protected int $iterations = 10;

View File

@ -27,6 +27,7 @@ use App\Models\Credit;
use App\Models\Expense; use App\Models\Expense;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\User; use App\Models\User;
use App\Repositories\InvoiceRepository;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Routing\Middleware\ThrottleRequests; use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
@ -649,9 +650,27 @@ $this->account->forceDelete();
'public_notes' => 'Public5', 'public_notes' => 'Public5',
'private_notes' => 'Private5', 'private_notes' => 'Private5',
'terms' => 'Terms5', 'terms' => 'Terms5',
'tax_rate1' => 0,
'tax_rate2' => 0,
'tax_rate3' => 0,
'discount' => 0,
'line_items' => [
[
'quantity' => 1,
'product_key' => 'product_key',
'notes' => 'notes',
'cost' => 1000,
'custom_value1' => 'Custom 1',
'custom_value2' => 'Custom 2',
'custom_value3' => 'Custom 3',
]
]
]); ]);
$repo = new InvoiceRepository();
$invoice = $repo->save([], $invoice);
$log = '[[1689547165,1689550765,"sumtin",true]]'; $log = '[[1689547165,1689550765,"sumtin",true]]';
\App\Models\Task::factory()->create([ \App\Models\Task::factory()->create([
@ -901,17 +920,36 @@ $this->account->forceDelete();
'balance' => 100, 'balance' => 100,
'number' => '12345', 'number' => '12345',
'status_id' => 2, 'status_id' => 2,
'discount' => 10, 'discount' => 0,
'po_number' => '1234', 'po_number' => '1234',
'public_notes' => 'Public', 'public_notes' => 'Public',
'private_notes' => 'Private', 'private_notes' => 'Private',
'terms' => 'Terms', 'terms' => 'Terms',
'tax_rate1' => 0,
'tax_rate2' => 0,
'tax_rate3' => 0,
'line_items' => [
[
'quantity' => 1,
'product_key' => 'product_key',
'notes' => 'notes',
'cost' => 100,
'custom_value1' => 'Custom 1',
'custom_value2' => 'Custom 2',
'custom_value3' => 'Custom 3',
]
]
]); ]);
$invoice->client->balance = 100; $invoice->client->balance = 100;
$invoice->client->paid_to_date = 0; $invoice->client->paid_to_date = 0;
$invoice->push(); $invoice->push();
$repo = new InvoiceRepository();
$invoice = $repo->save([], $invoice);
$invoice->service()->markPaid()->save(); $invoice->service()->markPaid()->save();
$data = [ $data = [
@ -1141,6 +1179,8 @@ $this->account->forceDelete();
public function testCreditJsonReport() public function testCreditJsonReport()
{ {
config(['queue.default' => 'redis']);
Credit::factory()->create([ Credit::factory()->create([
'user_id' => $this->user->id, 'user_id' => $this->user->id,
'company_id' => $this->company->id, 'company_id' => $this->company->id,
@ -1167,7 +1207,6 @@ $this->account->forceDelete();
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/reports/credits?output=json', $data); ])->postJson('/api/v1/reports/credits?output=json', $data);
$response->assertStatus(200); $response->assertStatus(200);
$arr = $response->json(); $arr = $response->json();
@ -1179,7 +1218,6 @@ $this->account->forceDelete();
$response->assertStatus(409); $response->assertStatus(409);
$this->account->forceDelete(); $this->account->forceDelete();
} }
@ -1247,7 +1285,7 @@ $this->account->forceDelete();
public function testInvoiceCustomColumnsCsvGeneration() public function testInvoiceCustomColumnsCsvGeneration()
{ {
\App\Models\Invoice::factory()->create([ $invoice = \App\Models\Invoice::factory()->create([
'user_id' => $this->user->id, 'user_id' => $this->user->id,
'company_id' => $this->company->id, 'company_id' => $this->company->id,
'client_id' => $this->client->id, 'client_id' => $this->client->id,
@ -1260,8 +1298,26 @@ $this->account->forceDelete();
'public_notes' => 'Public', 'public_notes' => 'Public',
'private_notes' => 'Private', 'private_notes' => 'Private',
'terms' => 'Terms', 'terms' => 'Terms',
'tax_rate1' => 0,
'tax_rate2' => 0,
'tax_rate3' => 0,
'line_items' => [
[
'quantity' => 1,
'product_key' => 'product_key',
'notes' => 'notes',
'cost' => 100,
'custom_value1' => 'Custom 1',
'custom_value2' => 'Custom 2',
'custom_value3' => 'Custom 3',
]
]
]); ]);
$repo = new InvoiceRepository();
$invoice = $repo->save([], $invoice);
$data = [ $data = [
'date_range' => 'all', 'date_range' => 'all',
'report_keys' => ["client.name","invoice.number","invoice.amount","payment.date", "payment.amount","invoice.user"], 'report_keys' => ["client.name","invoice.number","invoice.amount","payment.date", "payment.amount","invoice.user"],
@ -1419,7 +1475,7 @@ $this->account->forceDelete();
public function testInvoiceItemsCustomColumnsCsvGeneration() public function testInvoiceItemsCustomColumnsCsvGeneration()
{ {
\App\Models\Invoice::factory()->create([ $invoice = \App\Models\Invoice::factory()->create([
'user_id' => $this->user->id, 'user_id' => $this->user->id,
'company_id' => $this->company->id, 'company_id' => $this->company->id,
'client_id' => $this->client->id, 'client_id' => $this->client->id,
@ -1427,7 +1483,7 @@ $this->account->forceDelete();
'balance' => 50, 'balance' => 50,
'number' => '1234', 'number' => '1234',
'status_id' => 2, 'status_id' => 2,
'discount' => 10, 'discount' => 0,
'po_number' => '1234', 'po_number' => '1234',
'public_notes' => 'Public', 'public_notes' => 'Public',
'private_notes' => 'Private', 'private_notes' => 'Private',
@ -1452,6 +1508,9 @@ $this->account->forceDelete();
] ]
]); ]);
$repo = new InvoiceRepository();
$invoice = $repo->save([], $invoice);
$data = [ $data = [
'date_range' => 'all', 'date_range' => 'all',
'report_keys' => ["client.name","invoice.number","invoice.amount","payment.date", "payment.amount", "item.quantity", "item.cost", "item.line_total", "item.discount", "item.notes", "item.product_key", "item.custom_value1", "item.tax_name1", "item.tax_rate1",], 'report_keys' => ["client.name","invoice.number","invoice.amount","payment.date", "payment.amount", "item.quantity", "item.cost", "item.line_total", "item.discount", "item.notes", "item.product_key", "item.custom_value1", "item.tax_name1", "item.tax_rate1",],
@ -1583,7 +1642,6 @@ $this->account->forceDelete();
$csv = $response->body(); $csv = $response->body();
$this->assertEquals('bob', $this->getFirstValueByColumn($csv, 'Client Name')); $this->assertEquals('bob', $this->getFirstValueByColumn($csv, 'Client Name'));
$this->assertEquals('1234', $this->getFirstValueByColumn($csv, 'Quote Number')); $this->assertEquals('1234', $this->getFirstValueByColumn($csv, 'Quote Number'));
$this->assertEquals('10', $this->getFirstValueByColumn($csv, 'Item Quantity')); $this->assertEquals('10', $this->getFirstValueByColumn($csv, 'Item Quantity'));
@ -1596,7 +1654,6 @@ $this->account->forceDelete();
$this->assertEquals('GST', $this->getFirstValueByColumn($csv, 'Item Tax Name 1')); $this->assertEquals('GST', $this->getFirstValueByColumn($csv, 'Item Tax Name 1'));
$this->assertEquals('10', $this->getFirstValueByColumn($csv, 'Item Tax Rate 1')); $this->assertEquals('10', $this->getFirstValueByColumn($csv, 'Item Tax Rate 1'));
$data = [ $data = [
'date_range' => 'all', 'date_range' => 'all',
'report_keys' => $this->all_client_report_keys, 'report_keys' => $this->all_client_report_keys,
@ -1609,7 +1666,6 @@ $this->account->forceDelete();
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->post('/api/v1/reports/quote_items', $data)->assertStatus(200); ])->post('/api/v1/reports/quote_items', $data)->assertStatus(200);
$this->account->forceDelete(); $this->account->forceDelete();
} }
@ -1671,7 +1727,6 @@ $this->account->forceDelete();
$this->assertEquals('Private', $this->getFirstValueByColumn($csv, 'Purchase Order Private Notes')); $this->assertEquals('Private', $this->getFirstValueByColumn($csv, 'Purchase Order Private Notes'));
$this->assertEquals('Terms', $this->getFirstValueByColumn($csv, 'Purchase Order Terms')); $this->assertEquals('Terms', $this->getFirstValueByColumn($csv, 'Purchase Order Terms'));
$this->account->forceDelete(); $this->account->forceDelete();
} }
@ -1689,7 +1744,6 @@ $this->account->forceDelete();
] ]
); );
\App\Models\PurchaseOrder::factory()->create([ \App\Models\PurchaseOrder::factory()->create([
'user_id' => $this->user->id, 'user_id' => $this->user->id,
'company_id' => $this->company->id, 'company_id' => $this->company->id,
@ -1837,13 +1891,31 @@ $this->account->forceDelete();
'balance' => 100, 'balance' => 100,
'number' => '12345', 'number' => '12345',
'status_id' => 2, 'status_id' => 2,
'discount' => 10, 'discount' => 0,
'po_number' => '1234', 'po_number' => '1234',
'public_notes' => 'Public', 'public_notes' => 'Public',
'private_notes' => 'Private', 'private_notes' => 'Private',
'terms' => 'Terms', 'terms' => 'Terms',
'tax_rate1' => 0,
'tax_rate2' => 0,
'tax_rate3' => 0,
'line_items' => [
[
'quantity' => 1,
'product_key' => 'product_key',
'notes' => 'notes',
'cost' => 100,
'custom_value1' => 'Custom 1',
'custom_value2' => 'Custom 2',
'custom_value3' => 'Custom 3',
]
]
]); ]);
$repo = new InvoiceRepository();
$invoice = $repo->save([], $invoice);
$invoice->service()->markPaid()->save(); $invoice->service()->markPaid()->save();
$data = [ $data = [
@ -1956,6 +2028,20 @@ $this->account->forceDelete();
'public_notes' => 'Public', 'public_notes' => 'Public',
'private_notes' => 'Private', 'private_notes' => 'Private',
'terms' => 'Terms', 'terms' => 'Terms',
'tax_rate1' => 0,
'tax_rate2' => 0,
'tax_rate3' => 0,
'line_items' => [
[
'quantity' => 1,
'product_key' => 'product_key',
'notes' => 'notes',
'cost' => 110,
'custom_value1' => 'Custom 1',
'custom_value2' => 'Custom 2',
'custom_value3' => 'Custom 3',
]
]
]); ]);
$data = [ $data = [
@ -1971,8 +2057,6 @@ $this->account->forceDelete();
$response->assertStatus(200); $response->assertStatus(200);
$response->assertStatus(200);
$arr = $response->json(); $arr = $response->json();
$hash = $arr['message']; $hash = $arr['message'];
@ -2194,7 +2278,7 @@ $this->account->forceDelete();
public function testQuoteCsvGeneration() public function testQuoteCsvGeneration()
{ {
\App\Models\Quote::factory()->create([ $quote = \App\Models\Quote::factory()->create([
'user_id' => $this->user->id, 'user_id' => $this->user->id,
'company_id' => $this->company->id, 'company_id' => $this->company->id,
'client_id' => $this->client->id, 'client_id' => $this->client->id,

View File

@ -9,7 +9,7 @@
* @license https://www.elastic.co/licensing/elastic-license * @license https://www.elastic.co/licensing/elastic-license
*/ */
namespace Tests\Feature\Import\CSV; namespace Tests\Feature\Import\XLS;
use App\Import\Providers\Csv; use App\Import\Providers\Csv;
use App\Import\Transformer\BaseTransformer; use App\Import\Transformer\BaseTransformer;

View File

@ -11,13 +11,14 @@
namespace Tests\Feature; namespace Tests\Feature;
use App\Jobs\Util\UpdateExchangeRates;
use App\Libraries\Currency\Conversion\CurrencyApi;
use App\Models\Currency;
use App\Utils\Traits\MakesHash;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\MockAccountData;
use Tests\TestCase; use Tests\TestCase;
use App\Models\Currency;
use Tests\MockAccountData;
use App\Utils\Traits\MakesHash;
use App\Jobs\Util\UpdateExchangeRates;
use Illuminate\Support\Facades\Artisan;
use App\Libraries\Currency\Conversion\CurrencyApi;
use Illuminate\Foundation\Testing\DatabaseTransactions;
/** /**
* *
@ -26,8 +27,6 @@ use Tests\TestCase;
class UpdateExchangeRatesTest extends TestCase class UpdateExchangeRatesTest extends TestCase
{ {
use MakesHash; use MakesHash;
use DatabaseTransactions;
use MockAccountData;
protected function setUp(): void protected function setUp(): void
{ {
@ -37,6 +36,10 @@ class UpdateExchangeRatesTest extends TestCase
$this->markTestSkipped("no currency key set"); $this->markTestSkipped("no currency key set");
} }
if (Currency::count() == 0) {
Artisan::call('db:seed', ['--force' => true]);
}
} }
public function testExchangeRate() public function testExchangeRate()
@ -48,12 +51,11 @@ class UpdateExchangeRatesTest extends TestCase
$currency_api = json_decode($response->getBody()); $currency_api = json_decode($response->getBody());
UpdateExchangeRates::dispatchSync(); (new UpdateExchangeRates())->handle();
$gbp_currency = app('currencies')->first(function ($item) { $gbp_currency = \App\Models\Currency::find(2);
return $item->id == 2;
});
$this->assertNotNull($gbp_currency);
$this->assertEquals($currency_api->rates->GBP, $gbp_currency->exchange_rate); $this->assertEquals($currency_api->rates->GBP, $gbp_currency->exchange_rate);
} }

View File

@ -11,59 +11,63 @@
namespace Tests; namespace Tests;
use App\DataMapper\ClientRegistrationFields; use App\Models\Task;
use App\DataMapper\ClientSettings; use App\Models\User;
use App\DataMapper\CompanySettings; use App\Models\Quote;
use App\Factory\CompanyUserFactory;
use App\Factory\CreditFactory;
use App\Factory\InvoiceFactory;
use App\Factory\InvoiceInvitationFactory;
use App\Factory\InvoiceItemFactory;
use App\Factory\InvoiceToRecurringInvoiceFactory;
use App\Factory\PurchaseOrderFactory;
use App\Helpers\Invoice\InvoiceSum;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Models\Account;
use App\Models\BankIntegration;
use App\Models\BankTransaction;
use App\Models\BankTransactionRule;
use App\Models\Client; use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Company;
use App\Models\CompanyGateway;
use App\Models\CompanyToken;
use App\Models\Country;
use App\Models\Credit; use App\Models\Credit;
use App\Models\CreditInvitation; use App\Models\Vendor;
use App\Models\Account;
use App\Models\Company;
use App\Models\Country;
use App\Models\Expense; use App\Models\Expense;
use App\Models\ExpenseCategory;
use App\Models\GroupSetting;
use App\Models\InvoiceInvitation;
use App\Models\Payment; use App\Models\Payment;
use App\Models\Product; use App\Models\Product;
use App\Models\Project; use App\Models\Project;
use App\Models\PurchaseOrderInvitation; use App\Models\TaxRate;
use App\Models\Quote; use App\Models\Currency;
use App\Models\Scheduler;
use App\Models\TaskStatus;
use App\Utils\TruthSource;
use App\Models\CompanyToken;
use App\Models\GroupSetting;
use App\Models\ClientContact;
use App\Models\VendorContact;
use App\Factory\CreditFactory;
use App\Models\CompanyGateway;
use App\Models\RecurringQuote;
use Illuminate\Support\Carbon;
use App\Factory\InvoiceFactory;
use App\Models\BankIntegration;
use App\Models\BankTransaction;
use App\Models\ExpenseCategory;
use App\Models\QuoteInvitation; use App\Models\QuoteInvitation;
use App\Utils\Traits\MakesHash;
use App\Models\CreditInvitation;
use App\Models\RecurringExpense; use App\Models\RecurringExpense;
use App\Models\RecurringInvoice; use App\Models\RecurringInvoice;
use App\Models\RecurringQuote; use App\Models\InvoiceInvitation;
use App\Models\Scheduler; use App\DataMapper\ClientSettings;
use App\Models\Task; use App\DataMapper\CompanySettings;
use App\Models\TaskStatus; use App\Factory\CompanyUserFactory;
use App\Models\TaxRate; use App\Factory\InvoiceItemFactory;
use App\Models\User; use App\Helpers\Invoice\InvoiceSum;
use App\Models\Vendor; use App\Models\BankTransactionRule;
use App\Models\VendorContact;
use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesHash;
use App\Utils\TruthSource;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use App\Factory\PurchaseOrderFactory;
use Illuminate\Support\Facades\Cache;
use App\Utils\Traits\GeneratesCounter;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use App\Models\PurchaseOrderInvitation;
use App\Repositories\InvoiceRepository;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use App\Factory\InvoiceInvitationFactory;
use App\DataMapper\ClientRegistrationFields;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Repositories\RecurringInvoiceRepository;
use App\Factory\InvoiceToRecurringInvoiceFactory;
use App\Repositories\CreditRepository;
/** /**
* Class MockAccountData. * Class MockAccountData.
@ -208,6 +212,14 @@ trait MockAccountData
Artisan::call('db:seed', ['--force' => true]); Artisan::call('db:seed', ['--force' => true]);
} }
app()->singleton('currencies', function ($app) {
$resource = Currency::query()->orderBy('name')->get();
Cache::forever('currencies', $resource);
return $resource;
});
$this->faker = \Faker\Factory::create(); $this->faker = \Faker\Factory::create();
$fake_email = $this->faker->email(); $fake_email = $this->faker->email();
@ -309,9 +321,13 @@ trait MockAccountData
'company_id' => $this->company->id, 'company_id' => $this->company->id,
]); ]);
// $client_settings = ClientSettings::defaults();
// $client_settings->currency_id = '1';
$this->client = Client::factory()->create([ $this->client = Client::factory()->create([
'user_id' => $user_id, 'user_id' => $user_id,
'company_id' => $this->company->id, 'company_id' => $this->company->id,
// 'settings' => $client_settings,
]); ]);
Storage::makeDirectory($this->company->company_key.'/'.$this->client->client_hash.'/invoices', 0755, true); Storage::makeDirectory($this->company->company_key.'/'.$this->client->client_hash.'/invoices', 0755, true);
@ -439,6 +455,9 @@ trait MockAccountData
'uses_inclusive_taxes' => false, 'uses_inclusive_taxes' => false,
]); ]);
$repo = new RecurringInvoiceRepository();
$this->recurring_invoice = $repo->save([], $this->recurring_invoice);
$this->recurring_invoice_calc = new InvoiceSum($this->recurring_invoice); $this->recurring_invoice_calc = new InvoiceSum($this->recurring_invoice);
$this->recurring_invoice_calc->build(); $this->recurring_invoice_calc->build();
$this->recurring_invoice = $this->recurring_invoice_calc->getRecurringInvoice(); $this->recurring_invoice = $this->recurring_invoice_calc->getRecurringInvoice();
@ -480,6 +499,9 @@ trait MockAccountData
$this->invoice->fresh()->service()->markSent(); $this->invoice->fresh()->service()->markSent();
// $this->invoice->service()->markSent(); // $this->invoice->service()->markSent();
$repo = new InvoiceRepository();
$this->invoice = $repo->save([], $this->invoice);
$this->quote = Quote::factory()->create([ $this->quote = Quote::factory()->create([
'user_id' => $user_id, 'user_id' => $user_id,
'client_id' => $this->client->id, 'client_id' => $this->client->id,
@ -541,27 +563,30 @@ trait MockAccountData
$this->credit->number = $this->getNextCreditNumber($this->client, $this->credit); $this->credit->number = $this->getNextCreditNumber($this->client, $this->credit);
CreditInvitation::factory()->create([ // CreditInvitation::factory()->create([
'user_id' => $user_id, // 'user_id' => $user_id,
'company_id' => $this->company->id, // 'company_id' => $this->company->id,
'client_contact_id' => $contact->id, // 'client_contact_id' => $contact->id,
'credit_id' => $this->credit->id, // 'credit_id' => $this->credit->id,
]); // ]);
CreditInvitation::factory()->create([ // CreditInvitation::factory()->create([
'user_id' => $user_id, // 'user_id' => $user_id,
'company_id' => $this->company->id, // 'company_id' => $this->company->id,
'client_contact_id' => $contact2->id, // 'client_contact_id' => $contact2->id,
'credit_id' => $this->credit->id, // 'credit_id' => $this->credit->id,
]); // ]);
$this->credit->setRelation('client', $this->client); // $this->credit->setRelation('client', $this->client);
$this->credit->setRelation('company', $this->company); // $this->credit->setRelation('company', $this->company);
$this->credit->save(); // $this->credit->save();
$this->credit->service()->createInvitations()->markSent(); $repo = new CreditRepository();
$repo->save([], $this->credit);
// $this->credit->service()->createInvitations()->markSent();
// $this->credit->save();
$this->purchase_order = PurchaseOrderFactory::create($this->company->id, $user_id); $this->purchase_order = PurchaseOrderFactory::create($this->company->id, $user_id);
$this->purchase_order->vendor_id = $this->vendor->id; $this->purchase_order->vendor_id = $this->vendor->id;
@ -625,19 +650,25 @@ trait MockAccountData
$this->credit->ledger()->updateCreditBalance($this->credit->balance)->save(); $this->credit->ledger()->updateCreditBalance($this->credit->balance)->save();
$this->credit->number = $this->getNextCreditNumber($this->client, $this->credit); $this->credit->number = $this->getNextCreditNumber($this->client, $this->credit);
CreditInvitation::factory()->create([ $this->credit->save();
'user_id' => $user_id,
'company_id' => $this->company->id,
'client_contact_id' => $contact->id,
'credit_id' => $this->credit->id,
]);
CreditInvitation::factory()->create([
'user_id' => $user_id, $repo = new CreditRepository();
'company_id' => $this->company->id, $repo->save([], $this->credit);
'client_contact_id' => $contact2->id,
'credit_id' => $this->credit->id, // CreditInvitation::factory()->create([
]); // 'user_id' => $user_id,
// 'company_id' => $this->company->id,
// 'client_contact_id' => $contact->id,
// 'credit_id' => $this->credit->id,
// ]);
// CreditInvitation::factory()->create([
// 'user_id' => $user_id,
// 'company_id' => $this->company->id,
// 'client_contact_id' => $contact2->id,
// 'credit_id' => $this->credit->id,
// ]);
$this->bank_integration = BankIntegration::factory()->create([ $this->bank_integration = BankIntegration::factory()->create([
'user_id' => $user_id, 'user_id' => $user_id,
@ -680,17 +711,17 @@ trait MockAccountData
'company_id' => $this->company->id, 'company_id' => $this->company->id,
]); ]);
$invitations = CreditInvitation::whereCompanyId($this->credit->company_id) // $invitations = CreditInvitation::whereCompanyId($this->credit->company_id)
->whereCreditId($this->credit->id); // ->whereCreditId($this->credit->id);
$this->credit->setRelation('invitations', $invitations); // $this->credit->setRelation('invitations', $invitations);
$this->credit->service()->markSent(); // $this->credit->service()->markSent();
$this->credit->setRelation('client', $this->client); // $this->credit->setRelation('client', $this->client);
$this->credit->setRelation('company', $this->company); // $this->credit->setRelation('company', $this->company);
$this->credit->save(); // $this->credit->save();
$contacts = $this->invoice->client->contacts; $contacts = $this->invoice->client->contacts;

View File

@ -14,7 +14,6 @@ abstract class TestCase extends BaseTestCase
protected function setUp(): void protected function setUp(): void
{ {
parent::setUp(); parent::setUp();
} }
} }

View File

@ -14,6 +14,7 @@ namespace Tests\Unit;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\MockAccountData; use Tests\MockAccountData;
use Tests\TestCase; use Tests\TestCase;
use Illuminate\Support\Facades\Artisan;
/** /**
* *

View File

@ -14,13 +14,14 @@ namespace Tests\Unit\Chart;
use Tests\TestCase; use Tests\TestCase;
use App\Models\Client; use App\Models\Client;
use App\Models\Company; use App\Models\Company;
use App\Models\Expense;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Currency; use App\Models\Currency;
use Tests\MockAccountData; use Tests\MockAccountData;
use App\DataMapper\ClientSettings; use App\DataMapper\ClientSettings;
use App\DataMapper\CompanySettings; use App\DataMapper\CompanySettings;
use App\Models\Expense;
use App\Services\Chart\ChartService; use App\Services\Chart\ChartService;
use App\Repositories\InvoiceRepository;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
/** /**
@ -72,6 +73,8 @@ class ChartCurrencyTest extends TestCase
'user_id' => $this->user->id, 'user_id' => $this->user->id,
'company_id' => $company->id, 'company_id' => $company->id,
'settings' => $settings, 'settings' => $settings,
'balance' => 0,
'paid_to_date' => 0,
]); ]);
Currency::query()->where('id', 1)->update(['exchange_rate' => 1]); Currency::query()->where('id', 1)->update(['exchange_rate' => 1]);
@ -84,9 +87,10 @@ class ChartCurrencyTest extends TestCase
'user_id' => $this->user->id, 'user_id' => $this->user->id,
'company_id' => $company->id, 'company_id' => $company->id,
'settings' => $settings, 'settings' => $settings,
'balance' => 0,
'paid_to_date' => 0,
]); ]);
$i1 = Invoice::factory()->create([ $i1 = Invoice::factory()->create([
'client_id' => $usd->id, 'client_id' => $usd->id,
'user_id' => $this->user->id, 'user_id' => $this->user->id,
@ -96,7 +100,18 @@ class ChartCurrencyTest extends TestCase
'paid_to_date' => 0, 'paid_to_date' => 0,
'status_id' => 2, 'status_id' => 2,
'date' => now(), 'date' => now(),
'due_date' => now() 'due_date' => now(),
'line_items' => [
[
'product_key' => 'product_1',
'quantity' => 1,
'cost' => 100,
]
],
'tax_rate1' => 0,
'tax_rate2' => 0,
'tax_rate3' => 0,
'discount' => 0,
]); ]);
$i2 = Invoice::factory()->create([ $i2 = Invoice::factory()->create([
@ -108,19 +123,35 @@ class ChartCurrencyTest extends TestCase
'paid_to_date' => 0, 'paid_to_date' => 0,
'status_id' => 2, 'status_id' => 2,
'date' => now(), 'date' => now(),
'due_date' => now() 'due_date' => now(),
'line_items' => [
[
'product_key' => 'product_1',
'quantity' => 1,
'cost' => 100,
]
],
'tax_rate1' => 0,
'tax_rate2' => 0,
'tax_rate3' => 0,
'discount' => 0,
]); ]);
$repo = new InvoiceRepository();
$i1 = $repo->save([], $i1);
$i2 = $repo->save([], $i2);
$i1->service()->markPaid()->save(); $i1->service()->markPaid()->save();
$i2->service()->markPaid()->save(); $i2->service()->markPaid()->save();
$this->assertEquals(100, $i1->amount);
$this->assertEquals(100, $i2->amount);
$cs = new ChartService($company, $this->user, true); $cs = new ChartService($company, $this->user, true);
$results = $cs->totals('1970-01-01', '2050-01-01'); $results = $cs->totals('1970-01-01', '2050-01-01');
$this->assertCount(2, $results['currencies']); $this->assertCount(2, $results['currencies']);
// nlog($results);
$this->assertEquals('USD', $results['currencies'][1]); $this->assertEquals('USD', $results['currencies'][1]);
$this->assertEquals('GBP', $results['currencies'][2]); $this->assertEquals('GBP', $results['currencies'][2]);

View File

@ -17,6 +17,7 @@ use Tests\MockAccountData;
use App\Factory\InvoiceItemFactory; use App\Factory\InvoiceItemFactory;
use App\Factory\CloneQuoteToInvoiceFactory; use App\Factory\CloneQuoteToInvoiceFactory;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Database\Eloquent\Model;
/** /**
* *
@ -24,13 +25,15 @@ use Illuminate\Foundation\Testing\DatabaseTransactions;
class CloneQuoteToInvoiceFactoryTest extends TestCase class CloneQuoteToInvoiceFactoryTest extends TestCase
{ {
use MockAccountData; use MockAccountData;
// use DatabaseTransactions; use DatabaseTransactions;
protected function setUp(): void protected function setUp(): void
{ {
parent::setUp(); parent::setUp();
$this->makeTestData(); $this->makeTestData();
Model::reguard();
} }
public function testCloneItemSanityInvoice() public function testCloneItemSanityInvoice()

View File

@ -74,7 +74,6 @@ class EInvoiceTest extends TestCase
$data = $this->getExpiredPurchases([$company->company_key], true); $data = $this->getExpiredPurchases([$company->company_key], true);
$this->assertEquals(100, $data['purchased']); $this->assertEquals(100, $data['purchased']);
$this->assertEquals(-50, $data['sent'] + $data['received']); $this->assertEquals(-50, $data['sent'] + $data['received']);

View File

@ -89,17 +89,6 @@ class EntityTest extends TestCase
protected function tearDown(): void protected function tearDown(): void
{ {
// $this->company->company_users->each(function ($company_user) {
// $company_user->user->forceDelete();
// $company_user->forceDelete();
// });
// // Clean up any resources or reset state if necessary
// $this->account->delete();
parent::tearDown(); parent::tearDown();
} }
} }

View File

@ -422,13 +422,16 @@ class GeneratesCounterTest extends TestCase
public function testInvoiceNumberValue() public function testInvoiceNumberValue()
{ {
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($invoice_number, '0002'); $this->assertEquals('0002', $this->invoice->fresh()->number);
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh()); $invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($invoice_number, '0003'); $this->assertEquals($invoice_number, '0003');
$invoice_number = $this->getNextInvoiceNumber($this->client->fresh(), $this->invoice->fresh());
$this->assertEquals($invoice_number, '0004');
} }
public function testQuoteNumberValue() public function testQuoteNumberValue()
@ -605,11 +608,13 @@ class GeneratesCounterTest extends TestCase
$invoice_number = $this->getNextInvoiceNumber($cliz->fresh(), $this->invoice); $invoice_number = $this->getNextInvoiceNumber($cliz->fresh(), $this->invoice);
$this->assertEquals($invoice_number, '0002'); $this->assertEquals('0002', $this->invoice->fresh()->number);
$this->assertEquals('0003', $invoice_number);
$invoice_number = $this->getNextInvoiceNumber($cliz->fresh(), $this->invoice); $invoice_number = $this->getNextInvoiceNumber($cliz->fresh(), $this->invoice);
$this->assertEquals($invoice_number, '0003'); $this->assertEquals('0004', $invoice_number );
} }
public function testClientNumber() public function testClientNumber()

View File

@ -11,18 +11,20 @@
namespace Tests\Unit; namespace Tests\Unit;
use App\Factory\InvoiceInvitationFactory;
use App\Utils\Traits\MakesHash;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Validation\ValidationException;
use Tests\MockAccountData;
use Tests\TestCase; use Tests\TestCase;
use Tests\MockAccountData;
use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Model;
use App\Factory\InvoiceInvitationFactory;
use Illuminate\Validation\ValidationException;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class InvitationTest extends TestCase class InvitationTest extends TestCase
{ {
use MockAccountData; use MockAccountData;
// use DatabaseTransactions; use DatabaseTransactions;
use MakesHash; use MakesHash;
protected function setUp(): void protected function setUp(): void
@ -36,6 +38,8 @@ class InvitationTest extends TestCase
); );
$this->withoutExceptionHandling(); $this->withoutExceptionHandling();
Model::reguard();
} }
public function testInvitationSanity() public function testInvitationSanity()
@ -63,7 +67,7 @@ class InvitationTest extends TestCase
$response = $this->withHeaders([ $response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'), 'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->putJson('/api/v1/invoices/'.$this->invoice->hashed_id, $this->invoice->toArray()); ])->putJson('/api/v1/invoices/'.$this->invoice->hashed_id, $data);
$response->assertStatus(200); $response->assertStatus(200);

View File

@ -11,35 +11,113 @@
namespace Tests\Unit; namespace Tests\Unit;
use App\DataMapper\InvoiceItem; use Tests\TestCase;
use App\Factory\InvoiceFactory; use App\Models\User;
use App\Factory\InvoiceItemFactory; use App\Models\Account;
use App\Helpers\Invoice\InvoiceSum; use App\Models\Company;
use App\Helpers\Invoice\InvoiceSumInclusive;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Repositories\InvoiceRepository;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\MockAccountData; use Tests\MockAccountData;
use Tests\TestCase; use App\Models\CompanyToken;
use App\DataMapper\InvoiceItem;
use App\Factory\InvoiceFactory;
use App\DataMapper\CompanySettings;
use App\Factory\CompanyUserFactory;
use App\Factory\InvoiceItemFactory;
use App\Helpers\Invoice\InvoiceSum;
use App\Repositories\InvoiceRepository;
use Illuminate\Database\Eloquent\Model;
use App\Helpers\Invoice\InvoiceSumInclusive;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class InvoiceMarkPaidTest extends TestCase class InvoiceMarkPaidTest extends TestCase
{ {
use MockAccountData; use MockAccountData;
// use DatabaseTransactions; use DatabaseTransactions;
public $invoice; public $invoice;
public $company;
public $user;
public $payload;
public $account;
public $client;
public $token;
public $cu;
public $faker;
public function setUp(): void public function setUp(): void
{ {
parent::setUp(); parent::setUp();
$this->makeTestData(); // $this->makeTestData();
$this->faker = \Faker\Factory::create();
Model::reguard();
}
private function buildData()
{
if($this->account)
$this->account->forceDelete();
/** @var \App\Models\Account $account */
$this->account = Account::factory()->create([
'hosted_client_count' => 1000,
'hosted_company_count' => 1000,
]);
$this->account->num_users = 3;
$this->account->save();
$this->user = User::factory()->create([
'account_id' => $this->account->id,
'confirmation_code' => 'xyz123',
'email' => $this->faker->unique()->safeEmail(),
]);
$settings = CompanySettings::defaults();
$settings->client_online_payment_notification = false;
$settings->client_manual_payment_notification = false;
$this->company = Company::factory()->create([
'account_id' => $this->account->id,
'settings' => $settings,
]);
$this->company->settings = $settings;
$this->company->save();
$this->cu = CompanyUserFactory::create($this->user->id, $this->company->id, $this->account->id);
$this->cu->is_owner = true;
$this->cu->is_admin = true;
$this->cu->is_locked = false;
$this->cu->save();
$this->token = \Illuminate\Support\Str::random(64);
$company_token = new CompanyToken();
$company_token->user_id = $this->user->id;
$company_token->company_id = $this->company->id;
$company_token->account_id = $this->account->id;
$company_token->name = 'test token';
$company_token->token = $this->token;
$company_token->is_system = true;
$company_token->save();
} }
public function testInvoiceMarkPaidFromDraft() public function testInvoiceMarkPaidFromDraft()
{ {
$this->buildData();
$c = \App\Models\Client::factory()->create([ $c = \App\Models\Client::factory()->create([
'user_id' => $this->user->id, 'user_id' => $this->user->id,
@ -75,6 +153,9 @@ class InvoiceMarkPaidTest extends TestCase
$i->calc()->getInvoice(); $i->calc()->getInvoice();
$repo = new InvoiceRepository();
$repo->save([], $i);
$response = $this->withHeaders([ $response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'), 'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
@ -92,8 +173,7 @@ class InvoiceMarkPaidTest extends TestCase
$this->assertEquals(10, $i->paid_to_date); $this->assertEquals(10, $i->paid_to_date);
$this->assertEquals(4, $i->status_id); $this->assertEquals(4, $i->status_id);
$this->account->delete();
$c->forceDelete();
} }
@ -101,6 +181,7 @@ class InvoiceMarkPaidTest extends TestCase
public function testInvoiceMarkPaidFromDraftBulk() public function testInvoiceMarkPaidFromDraftBulk()
{ {
$this->buildData();
$c = \App\Models\Client::factory()->create([ $c = \App\Models\Client::factory()->create([
'user_id' => $this->user->id, 'user_id' => $this->user->id,
@ -137,6 +218,9 @@ class InvoiceMarkPaidTest extends TestCase
$i->calc()->getInvoice(); $i->calc()->getInvoice();
$repo = new InvoiceRepository();
$repo->save([], $i);
$data = [ $data = [
'action' => 'mark_paid', 'action' => 'mark_paid',
'ids' => [$i->hashed_id] 'ids' => [$i->hashed_id]
@ -161,6 +245,8 @@ class InvoiceMarkPaidTest extends TestCase
$c->forceDelete(); $c->forceDelete();
$this->account->delete();
} }
} }

View File

@ -53,7 +53,7 @@ class LateFeeTest extends TestCase
$this->makeTestData(); $this->makeTestData();
$this->withoutExceptionHandling(); // $this->withoutExceptionHandling();
} }
@ -271,6 +271,12 @@ class LateFeeTest extends TestCase
public function testLateFeeRemovals() public function testLateFeeRemovals()
{ {
if(!config('ninja.testvars.stripe')){
$this->markTestSkipped('Stripe is not enabled');
}
config(['queue.default' => 'sync']);
$data = []; $data = [];
$data[1]['min_limit'] = -1; $data[1]['min_limit'] = -1;
$data[1]['max_limit'] = -1; $data[1]['max_limit'] = -1;
@ -348,6 +354,8 @@ class LateFeeTest extends TestCase
$i = $i->calc()->getInvoice(); $i = $i->calc()->getInvoice();
$repo = new \App\Repositories\InvoiceRepository();
$repo->save([], $i);
$this->assertEquals(3, count($i->line_items)); $this->assertEquals(3, count($i->line_items));
$this->assertEquals(21, $i->amount); $this->assertEquals(21, $i->amount);
@ -370,7 +378,12 @@ class LateFeeTest extends TestCase
$this->assertEquals(2, count($i->line_items)); $this->assertEquals(2, count($i->line_items));
// try{
$i->service()->autoBill(); $i->service()->autoBill();
// }
// catch(\Exception $e){
// nlog($e->getMessage());
// }
$i = $i->fresh(); $i = $i->fresh();

View File

@ -1,12 +1 @@
<?php <?php
// This is global bootstrap for autoloading
use Codeception\Util\Fixtures;
Fixtures::add('url', 'http://localhost');
Fixtures::add('username', 'user@example.com');
Fixtures::add('password', 'password');
Fixtures::add('api_secret', 'password');
Fixtures::add('stripe_secret_key', 'sk_test_g888H1K4efDxHKj7fSFTBGgU');
Fixtures::add('stripe_publishable_key', '');