Merge pull request #8885 from turbo124/v5-develop

Fixes for Swiss QR Codes
This commit is contained in:
David Bomba 2023-10-18 20:08:58 +11:00 committed by GitHub
commit 45d5838aa8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 305 additions and 57 deletions

View File

@ -1 +1 @@
5.7.30 5.7.31

View File

@ -168,6 +168,7 @@ class BaseExport
'tax_rate1' => 'invoice.tax_rate1', 'tax_rate1' => 'invoice.tax_rate1',
'tax_rate2' => 'invoice.tax_rate2', 'tax_rate2' => 'invoice.tax_rate2',
'tax_rate3' => 'invoice.tax_rate3', 'tax_rate3' => 'invoice.tax_rate3',
'recurring_invoice' => 'invoice.recurring_id',
]; ];
protected array $recurring_invoice_report_keys = [ protected array $recurring_invoice_report_keys = [
@ -230,7 +231,7 @@ class BaseExport
'po_number' => 'purchase_order.po_number', 'po_number' => 'purchase_order.po_number',
'private_notes' => 'purchase_order.private_notes', 'private_notes' => 'purchase_order.private_notes',
'public_notes' => 'purchase_order.public_notes', 'public_notes' => 'purchase_order.public_notes',
'status' => 'purchase_order.status_id', 'status' => 'purchase_order.status',
'tax_name1' => 'purchase_order.tax_name1', 'tax_name1' => 'purchase_order.tax_name1',
'tax_name2' => 'purchase_order.tax_name2', 'tax_name2' => 'purchase_order.tax_name2',
'tax_name3' => 'purchase_order.tax_name3', 'tax_name3' => 'purchase_order.tax_name3',
@ -377,6 +378,7 @@ class BaseExport
"custom_value4" => "payment.custom_value4", "custom_value4" => "payment.custom_value4",
"user" => "payment.user_id", "user" => "payment.user_id",
"assigned_user" => "payment.assigned_user_id", "assigned_user" => "payment.assigned_user_id",
]; ];
protected array $expense_report_keys = [ protected array $expense_report_keys = [
@ -429,6 +431,14 @@ class BaseExport
'project' => 'task.project_id', 'project' => 'task.project_id',
]; ];
protected array $forced_client_fields = [
"client.name",
];
protected array $forced_vendor_fields = [
"vendor.name",
];
protected function filterByClients($query) protected function filterByClients($query)
{ {
if (isset($this->input['client_id']) && $this->input['client_id'] != 'all') { if (isset($this->input['client_id']) && $this->input['client_id'] != 'all') {

View File

@ -93,6 +93,8 @@ class CreditExport extends BaseExport
$this->input['report_keys'] = array_values($this->credit_report_keys); $this->input['report_keys'] = array_values($this->credit_report_keys);
} }
$this->input['report_keys'] = array_merge($this->input['report_keys'], array_diff($this->forced_client_fields, $this->input['report_keys']));
$query = Credit::query() $query = Credit::query()
->withTrashed() ->withTrashed()
->with('client') ->with('client')

View File

@ -50,6 +50,8 @@ class InvoiceExport extends BaseExport
$this->input['report_keys'] = array_values($this->invoice_report_keys); $this->input['report_keys'] = array_values($this->invoice_report_keys);
} }
$this->input['report_keys'] = array_merge($this->input['report_keys'], array_diff($this->forced_client_fields, $this->input['report_keys']));
$query = Invoice::query() $query = Invoice::query()
->withTrashed() ->withTrashed()
->with('client') ->with('client')
@ -143,6 +145,11 @@ class InvoiceExport extends BaseExport
$entity['invoice.status'] = $invoice->stringStatus($invoice->status_id); $entity['invoice.status'] = $invoice->stringStatus($invoice->status_id);
} }
if (in_array('invoice.recurring_id', $this->input['report_keys'])) {
$entity['invoice.recurring_id'] = $invoice->recurring_invoice->number ?? '';
}
return $entity; return $entity;
} }
} }

View File

@ -62,6 +62,8 @@ class InvoiceItemExport extends BaseExport
$this->input['report_keys'] = array_values($this->mergeItemsKeys('invoice_report_keys')); $this->input['report_keys'] = array_values($this->mergeItemsKeys('invoice_report_keys'));
} }
$this->input['report_keys'] = array_merge($this->input['report_keys'], array_diff($this->forced_client_fields, $this->input['report_keys']));
$query = Invoice::query() $query = Invoice::query()
->withTrashed() ->withTrashed()
->with('client') ->with('client')
@ -200,6 +202,27 @@ class InvoiceItemExport extends BaseExport
$entity['tax_category'] = $invoice->taxTypeString($entity['tax_category']); $entity['tax_category'] = $invoice->taxTypeString($entity['tax_category']);
} }
if (in_array('invoice.country_id', $this->input['report_keys'])) {
$entity['invoice.country_id'] = $invoice->client->country ? ctrans("texts.country_{$invoice->client->country->name}") : '';
}
if (in_array('invoice.currency_id', $this->input['report_keys'])) {
$entity['invoice.currency_id'] = $invoice->client->currency() ? $invoice->client->currency()->code : $invoice->company->currency()->code;
}
if (in_array('invoice.client_id', $this->input['report_keys'])) {
$entity['invoice.client_id'] = $invoice->client->present()->name();
}
if (in_array('invoice.status', $this->input['report_keys'])) {
$entity['invoice.status'] = $invoice->stringStatus($invoice->status_id);
}
if (in_array('invoice.recurring_id', $this->input['report_keys'])) {
$entity['invoice.recurring_id'] = $invoice->recurring_invoice->number ?? '';
}
return $entity; return $entity;
} }

View File

@ -48,6 +48,8 @@ class PaymentExport extends BaseExport
$this->input['report_keys'] = array_values($this->payment_report_keys); $this->input['report_keys'] = array_values($this->payment_report_keys);
} }
$this->input['report_keys'] = array_merge($this->input['report_keys'], array_diff($this->forced_client_fields, $this->input['report_keys']));
$query = Payment::query() $query = Payment::query()
->withTrashed() ->withTrashed()
->where('company_id', $this->company->id) ->where('company_id', $this->company->id)
@ -69,6 +71,8 @@ class PaymentExport extends BaseExport
return ['identifier' => $key, 'display_value' => $headerdisplay[$value]]; return ['identifier' => $key, 'display_value' => $headerdisplay[$value]];
})->toArray(); })->toArray();
nlog($header);
$report = $query->cursor() $report = $query->cursor()
->map(function ($resource) { ->map(function ($resource) {
$row = $this->buildRow($resource); $row = $this->buildRow($resource);

View File

@ -54,7 +54,7 @@ class PurchaseOrderExport extends BaseExport
'po_number' => 'purchase_order.po_number', 'po_number' => 'purchase_order.po_number',
'private_notes' => 'purchase_order.private_notes', 'private_notes' => 'purchase_order.private_notes',
'public_notes' => 'purchase_order.public_notes', 'public_notes' => 'purchase_order.public_notes',
'status' => 'purchase_order.status_id', 'status' => 'purchase_order.status',
'tax_name1' => 'purchase_order.tax_name1', 'tax_name1' => 'purchase_order.tax_name1',
'tax_name2' => 'purchase_order.tax_name2', 'tax_name2' => 'purchase_order.tax_name2',
'tax_name3' => 'purchase_order.tax_name3', 'tax_name3' => 'purchase_order.tax_name3',
@ -95,6 +95,9 @@ class PurchaseOrderExport extends BaseExport
if (count($this->input['report_keys']) == 0) { if (count($this->input['report_keys']) == 0) {
$this->input['report_keys'] = array_values($this->purchase_order_report_keys); $this->input['report_keys'] = array_values($this->purchase_order_report_keys);
} }
$this->input['report_keys'] = array_merge($this->input['report_keys'], array_diff($this->forced_vendor_fields, $this->input['report_keys']));
$query = PurchaseOrder::query() $query = PurchaseOrder::query()
->withTrashed() ->withTrashed()
->with('vendor') ->with('vendor')
@ -181,8 +184,8 @@ class PurchaseOrderExport extends BaseExport
$entity['vendor'] = $purchase_order->vendor->present()->name(); $entity['vendor'] = $purchase_order->vendor->present()->name();
} }
if (in_array('status_id', $this->input['report_keys'])) { if (in_array('purchase_order.status', $this->input['report_keys'])) {
$entity['status'] = $purchase_order->stringStatus($purchase_order->status_id); $entity['purchase_order.status'] = $purchase_order->stringStatus($purchase_order->status_id);
} }
return $entity; return $entity;

View File

@ -55,6 +55,8 @@ class PurchaseOrderItemExport extends BaseExport
$this->input['report_keys'] = array_values($this->mergeItemsKeys('purchase_order_report_keys')); $this->input['report_keys'] = array_values($this->mergeItemsKeys('purchase_order_report_keys'));
} }
$this->input['report_keys'] = array_merge($this->input['report_keys'], array_diff($this->forced_vendor_fields, $this->input['report_keys']));
$query = PurchaseOrder::query() $query = PurchaseOrder::query()
->withTrashed() ->withTrashed()
->with('vendor')->where('company_id', $this->company->id) ->with('vendor')->where('company_id', $this->company->id)

View File

@ -56,6 +56,8 @@ class QuoteExport extends BaseExport
$this->input['report_keys'] = array_values($this->quote_report_keys); $this->input['report_keys'] = array_values($this->quote_report_keys);
} }
$this->input['report_keys'] = array_merge($this->input['report_keys'], array_diff($this->forced_client_fields, $this->input['report_keys']));
$query = Quote::query() $query = Quote::query()
->withTrashed() ->withTrashed()
->with('client') ->with('client')

View File

@ -57,6 +57,8 @@ class QuoteItemExport extends BaseExport
$this->input['report_keys'] = array_values($this->mergeItemsKeys('quote_report_keys')); $this->input['report_keys'] = array_values($this->mergeItemsKeys('quote_report_keys'));
} }
$this->input['report_keys'] = array_merge($this->input['report_keys'], array_diff($this->forced_client_fields, $this->input['report_keys']));
$query = Quote::query() $query = Quote::query()
->withTrashed() ->withTrashed()
->with('client')->where('company_id', $this->company->id) ->with('client')->where('company_id', $this->company->id)

View File

@ -48,6 +48,8 @@ class RecurringInvoiceExport extends BaseExport
$this->input['report_keys'] = array_values($this->recurring_invoice_report_keys); $this->input['report_keys'] = array_values($this->recurring_invoice_report_keys);
} }
$this->input['report_keys'] = array_merge($this->input['report_keys'], array_diff($this->forced_client_fields, $this->input['report_keys']));
$query = RecurringInvoice::query() $query = RecurringInvoice::query()
->withTrashed() ->withTrashed()
->with('client') ->with('client')
@ -135,8 +137,8 @@ class RecurringInvoiceExport extends BaseExport
$entity['client'] = $invoice->client->present()->name(); $entity['client'] = $invoice->client->present()->name();
} }
if (in_array('status_id', $this->input['report_keys'])) { if (in_array('recurring_invoice.status', $this->input['report_keys'])) {
$entity['status'] = $invoice->stringStatus($invoice->status_id); $entity['recurring_invoice.status'] = $invoice->stringStatus($invoice->status_id);
} }
if (in_array('project_id', $this->input['report_keys'])) { if (in_array('project_id', $this->input['report_keys'])) {

View File

@ -60,6 +60,8 @@ class TaskExport extends BaseExport
$this->input['report_keys'] = array_values($this->task_report_keys); $this->input['report_keys'] = array_values($this->task_report_keys);
} }
$this->input['report_keys'] = array_merge($this->input['report_keys'], array_diff($this->forced_client_fields, $this->input['report_keys']));
$query = Task::query() $query = Task::query()
->withTrashed() ->withTrashed()
->where('company_id', $this->company->id) ->where('company_id', $this->company->id)

View File

@ -28,6 +28,7 @@ class UserFactory
$user->failed_logins = 0; $user->failed_logins = 0;
$user->signature = ''; $user->signature = '';
$user->theme_id = 0; $user->theme_id = 0;
$user->user_logged_in_notification = true;
return $user; return $user;
} }

View File

@ -50,7 +50,8 @@ class EpcQrGenerator
); );
$writer = new Writer($renderer); $writer = new Writer($renderer);
$this->validateFields(); if($this->validateFields())
return '';
$qr = $writer->writeString($this->encodeMessage(), 'utf-8'); $qr = $writer->writeString($this->encodeMessage(), 'utf-8');
@ -87,12 +88,16 @@ class EpcQrGenerator
private function validateFields() private function validateFields()
{ {
if (Ninja::isSelfHost() && isset($this->company?->custom_fields?->company2)) { if (Ninja::isSelfHost() && isset($this->company?->custom_fields?->company2)) {
return true;
nlog('The BIC field is not present and _may_ be a required fields for EPC QR codes'); nlog('The BIC field is not present and _may_ be a required fields for EPC QR codes');
} }
if (Ninja::isSelfHost() && isset($this->company?->custom_fields?->company1)) { if (Ninja::isSelfHost() && isset($this->company?->custom_fields?->company1)) {
return true;
nlog('The IBAN field is required'); nlog('The IBAN field is required');
} }
return false;
} }
private function formatMoney($value) private function formatMoney($value)

View File

@ -174,9 +174,14 @@ class SwissQrGenerator
return $html; return $html;
} catch (\Exception $e) { } catch (\Exception $e) {
foreach ($qrBill->getViolations() as $key => $violation) {
nlog("qr"); if(is_iterable($qrBill->getViolations())) {
nlog($violation);
foreach ($qrBill->getViolations() as $key => $violation) {
nlog("qr");
nlog($violation);
}
} }
nlog($e->getMessage()); nlog($e->getMessage());

View File

@ -31,11 +31,11 @@ class EmailHistoryController extends BaseController
->where('category_id', SystemLog::CATEGORY_MAIL) ->where('category_id', SystemLog::CATEGORY_MAIL)
->orderBy('id', 'DESC') ->orderBy('id', 'DESC')
->cursor() ->cursor()
->map(function ($system_log) { ->filter(function ($system_log) {
if(($system_log->log['history'] && $system_log->log['history']['events'] && count($system_log->log['history']['events']) >=1) ?? false) { return ($system_log->log['history'] && isset($system_log->log['history']['events']) && count($system_log->log['history']['events']) >=1) !== false;
return $system_log->log['history']; })->map(function ($system_log) {
} return $system_log->log['history'];
}); })->values()->all();
return response()->json($data, 200); return response()->json($data, 200);
@ -51,16 +51,17 @@ class EmailHistoryController extends BaseController
/** @var \App\Models\User $user */ /** @var \App\Models\User $user */
$user = auth()->user(); $user = auth()->user();
$data = SystemLog::where('company_id', $user->company()->id) $data = SystemLog::where('company_id', $user->company()->id)
->where('category_id', SystemLog::CATEGORY_MAIL) ->where('category_id', SystemLog::CATEGORY_MAIL)
->whereJsonContains('log->history->entity_id', $this->encodePrimaryKey($request->entity_id)) ->whereJsonContains('log->history->entity_id', $this->encodePrimaryKey($request->entity_id))
->orderBy('id', 'DESC') ->orderBy('id', 'DESC')
->cursor() ->cursor()
->map(function ($system_log) { ->filter(function ($system_log) {
if(($system_log->log['history'] && $system_log->log['history']['events'] && count($system_log->log['history']['events']) >=1) ?? false) { return ($system_log->log['history'] && isset($system_log->log['history']['events']) && count($system_log->log['history']['events']) >=1) !== false;
return $system_log->log['history']; })->map(function ($system_log) {
} return $system_log->log['history'];
}); })->values()->all();
return response()->json($data, 200); return response()->json($data, 200);

View File

@ -157,6 +157,7 @@ class RecurringInvoiceController extends BaseController
$user = auth()->user(); $user = auth()->user();
$recurring_invoice = RecurringInvoiceFactory::create($user->company()->id, $user->id); $recurring_invoice = RecurringInvoiceFactory::create($user->company()->id, $user->id);
$recurring_invoice->auto_bill = $user->company()->settings->auto_bill;
return $this->itemResponse($recurring_invoice); return $this->itemResponse($recurring_invoice);
} }

View File

@ -39,11 +39,11 @@ class TasksTable extends Component
->where('is_deleted', false) ->where('is_deleted', false)
->where('client_id', auth()->guard('contact')->user()->client_id); ->where('client_id', auth()->guard('contact')->user()->client_id);
if ($this->company->getSetting('show_all_tasks_client_portal') === 'invoiced') { if ( auth()->guard('contact')->user()->client->getSetting('show_all_tasks_client_portal') === 'invoiced') {
$query = $query->whereNotNull('invoice_id'); $query = $query->whereNotNull('invoice_id');
} }
if ($this->company->getSetting('show_all_tasks_client_portal') === 'uninvoiced') { if ( auth()->guard('contact')->user()->client->getSetting('show_all_tasks_client_portal') === 'uninvoiced') {
$query = $query->whereNull('invoice_id'); $query = $query->whereNull('invoice_id');
} }

View File

@ -180,7 +180,7 @@ class Request extends FormRequest
} }
//Filter the client contact password - if it is sent with ***** we should ignore it! //Filter the client contact password - if it is sent with ***** we should ignore it!
if (isset($contact['password'])) { if (isset($contact['password']) && is_string($contact['password'])) {
if (strlen($contact['password']) == 0) { if (strlen($contact['password']) == 0) {
$input['contacts'][$key]['password'] = ''; $input['contacts'][$key]['password'] = '';
} else { } else {

View File

@ -32,12 +32,12 @@ class NinjaMailerObject
/* Variable for cascading notifications */ /* Variable for cascading notifications */
public $entity_string = false; public $entity_string = false;
/* @var bool | App\Models\InvoiceInvitation | app\Models\QuoteInvitation | app\Models\CreditInvitation | app\Models\RecurringInvoiceInvitation | app\Models\PurchaseOrderInvitation $invitation*/ /* @var bool | App\Models\InvoiceInvitation | App\Models\QuoteInvitation | App\Models\CreditInvitation | App\Models\RecurringInvoiceInvitation | App\Models\PurchaseOrderInvitation $invitation*/
public $invitation = false; public $invitation = false;
public $template = false; public $template = false;
/* @var bool | App\Models\Invoice | app\Models\Quote | app\Models\Credit | app\Models\RecurringInvoice | app\Models\PurchaseOrder $invitation*/ /* @var bool | App\Models\Invoice | App\Models\Quote | App\Models\Credit | App\Models\RecurringInvoice | App\Models\PurchaseOrder | App\Models\Payment $entity */
public $entity = false; public $entity = false;
public $reminder_template = ''; public $reminder_template = '';

View File

@ -55,7 +55,7 @@ class UpdateUserLastLogin implements ShouldQueue
$key = "user_logged_in_{$user->id}{$event->company->db}"; $key = "user_logged_in_{$user->id}{$event->company->db}";
if ($user->ip != $ip && is_null(Cache::get($key))) { if ($user->ip != $ip && is_null(Cache::get($key)) && $user->user_logged_in_notification) {
$nmo = new NinjaMailerObject; $nmo = new NinjaMailerObject;
$nmo->mailable = new UserLoggedIn($user, $user->account->companies->first(), $ip); $nmo->mailable = new UserLoggedIn($user, $user->account->companies->first(), $ip);
$nmo->company = $user->account->companies->first(); $nmo->company = $user->account->companies->first();
@ -69,6 +69,7 @@ class UpdateUserLastLogin implements ShouldQueue
Cache::put($key, true, 60 * 24); Cache::put($key, true, 60 * 24);
$arr = json_encode(['ip' => $ip]); $arr = json_encode(['ip' => $ip]);
$arr = ctrans('texts.new_login_detected'). " {$ip}";
SystemLogger::dispatch( SystemLogger::dispatch(
$arr, $arr,

View File

@ -108,12 +108,15 @@ class TemplateEmail extends Mailable
if (strlen($settings->bcc_email) > 1) { if (strlen($settings->bcc_email) > 1) {
if (Ninja::isHosted()) { if (Ninja::isHosted()) {
$bccs = explode(',', str_replace(' ', '', $settings->bcc_email));
$this->bcc(array_slice($bccs, 0, 2)); if($company->account->isPaid()) {
//$this->bcc(reset($bccs)); //remove whitespace if any has been inserted. $bccs = explode(',', str_replace(' ', '', $settings->bcc_email));
$this->bcc(array_slice($bccs, 0, 5));
}
} else { } else {
$this->bcc(explode(',', str_replace(' ', '', $settings->bcc_email))); $this->bcc(explode(',', str_replace(' ', '', $settings->bcc_email)));
}//remove whitespace if any has been inserted. }
} }
$this->subject(str_replace("<br>", "", $this->build_email->getSubject())) $this->subject(str_replace("<br>", "", $this->build_email->getSubject()))

View File

@ -11,10 +11,11 @@
namespace App\Mail; namespace App\Mail;
use App\Utils\Ninja;
use App\Models\VendorContact; use App\Models\VendorContact;
use App\Services\PdfMaker\Designs\Utilities\DesignHelpers;
use App\Utils\VendorHtmlEngine;
use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailable;
use App\Utils\VendorHtmlEngine;
use App\Services\PdfMaker\Designs\Utilities\DesignHelpers;
class VendorTemplateEmail extends Mailable class VendorTemplateEmail extends Mailable
{ {
@ -102,8 +103,19 @@ class VendorTemplateEmail extends Mailable
$this->from(config('mail.from.address'), $email_from_name); $this->from(config('mail.from.address'), $email_from_name);
if (strlen($settings->bcc_email) > 1) { if (strlen($settings->bcc_email) > 1) {
$this->bcc(explode(',', str_replace(' ', '', $settings->bcc_email)));
}//remove whitespace if any has been inserted. if (Ninja::isHosted()) {
if($this->company->account->isPaid()) {
$bccs = explode(',', str_replace(' ', '', $settings->bcc_email));
$this->bcc(array_slice($bccs, 0, 5));
}
} else {
$this->bcc(explode(',', str_replace(' ', '', $settings->bcc_email)));
}
}
$this->subject($this->build_email->getSubject()) $this->subject($this->build_email->getSubject())
->text('email.template.text', [ ->text('email.template.text', [

View File

@ -23,7 +23,7 @@ class ClientPresenter extends EntityPresenter
*/ */
public function name() public function name()
{ {
if ($this->entity->name) { if (strlen($this->entity->name) > 1) {
return $this->entity->name; return $this->entity->name;
} }

View File

@ -73,7 +73,8 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
* @property int|null $deleted_at * @property int|null $deleted_at
* @property string|null $oauth_user_refresh_token * @property string|null $oauth_user_refresh_token
* @property string|null $last_confirmed_email_address * @property string|null $last_confirmed_email_address
* @property int $has_password * @property bool $has_password
* @property bool $user_logged_in_notification
* @property Carbon|null $oauth_user_token_expiry * @property Carbon|null $oauth_user_token_expiry
* @property string|null $sms_verification_code * @property string|null $sms_verification_code
* @property bool $verified_phone_number * @property bool $verified_phone_number
@ -140,6 +141,7 @@ class User extends Authenticatable implements MustVerifyEmail
* *
*/ */
protected $fillable = [ protected $fillable = [
'user_logged_in_notification',
'first_name', 'first_name',
'last_name', 'last_name',
'email', 'email',

View File

@ -251,7 +251,7 @@ class CreditCard
$response = $this->eway_driver->init()->eway->createTransaction(\Eway\Rapid\Enum\ApiMethod::DIRECT, $transaction); $response = $this->eway_driver->init()->eway->createTransaction(\Eway\Rapid\Enum\ApiMethod::DIRECT, $transaction);
if ($response->TransactionStatus) { if ($response->TransactionStatus ?? false) {
$this->logResponse($response, true); $this->logResponse($response, true);
$payment = $this->storePayment($response); $payment = $this->storePayment($response);
} else { } else {

View File

@ -134,8 +134,8 @@ class SquareWebhook implements ShouldQueue
nlog("Searching by payment hash"); nlog("Searching by payment hash");
$payment_hash_id = $apiResponse->getPayment()->getReferenceId() ?? false; $payment_hash_id = $apiResponse->getResult()->getPayment()->getReferenceId() ?? false;
$square_payment = $apiResponse->getPayment()->jsonSerialize(); $square_payment = $apiResponse->getResult()->getPayment()->jsonSerialize();
$payment_hash = PaymentHash::query()->where('hash', $payment_hash_id)->firstOrFail(); $payment_hash = PaymentHash::query()->where('hash', $payment_hash_id)->firstOrFail();
$payment_hash->data = array_merge((array) $payment_hash->data, (array)$square_payment); $payment_hash->data = array_merge((array) $payment_hash->data, (array)$square_payment);

View File

@ -140,7 +140,7 @@ class Email implements ShouldQueue
$this->email_object->client_id ? $this->email_object->settings = $this->email_object->client->getMergedSettings() : $this->email_object->settings = $this->company->settings; $this->email_object->client_id ? $this->email_object->settings = $this->email_object->client->getMergedSettings() : $this->email_object->settings = $this->company->settings;
$this->email_object->client_id ? nlog("client settings") : nlog("company settings "); // $this->email_object->client_id ? nlog("client settings") : nlog("company settings ");
$this->email_object->whitelabel = $this->company->account->isPaid() ? true : false; $this->email_object->whitelabel = $this->company->account->isPaid() ? true : false;
@ -413,6 +413,14 @@ class Email implements ShouldQueue
if ($address_object->address == " ") { if ($address_object->address == " ") {
return true; return true;
} }
if ($address_object->address == "") {
return true;
}
if($address_object->name == " " || $address_object->name == "") {
return true;
}
} }

View File

@ -255,8 +255,8 @@ class EmailDefaults
if (strlen($this->email->email_object->settings->bcc_email) > 1) { if (strlen($this->email->email_object->settings->bcc_email) > 1) {
if (Ninja::isHosted() && $this->email->company->account->isPaid()) { if (Ninja::isHosted() && $this->email->company->account->isPaid()) {
$bccs = array_slice(explode(',', str_replace(' ', '', $this->email->email_object->settings->bcc_email)), 0, 2); $bccs = array_slice(explode(',', str_replace(' ', '', $this->email->email_object->settings->bcc_email)), 0, 5);
} elseif (Ninja::isSelfHost()) { } else {
$bccs = (explode(',', str_replace(' ', '', $this->email->email_object->settings->bcc_email))); $bccs = (explode(',', str_replace(' ', '', $this->email->email_object->settings->bcc_email)));
} }
} }

View File

@ -120,7 +120,7 @@ class AutoBillInvoice extends AbstractService
/* Build payment hash */ /* Build payment hash */
$payment_hash = PaymentHash::create([ $payment_hash = PaymentHash::create([
'hash' => Str::random(64), 'hash' => Str::random(32),
'data' => [ 'data' => [
'amount_with_fee' => $amount + $fee, 'amount_with_fee' => $amount + $fee,
'invoices' => [ 'invoices' => [

View File

@ -75,7 +75,7 @@ class ZugferdEInvoice extends AbstractService
} else { } else {
$this->xrechnung->setDocumentBuyerReference($client->routing_id); $this->xrechnung->setDocumentBuyerReference($client->routing_id);
} }
if (!empty($client->shipping_address1)){ if (!empty($client->shipping_address1) && $client->shipping_country->exists()){
$this->xrechnung->setDocumentShipToAddress($client->shipping_address1, $client->shipping_address2, "", $client->shipping_postal_code, $client->shipping_city, $client->shipping_country->iso_3166_2, $client->shipping_state); $this->xrechnung->setDocumentShipToAddress($client->shipping_address1, $client->shipping_address2, "", $client->shipping_postal_code, $client->shipping_city, $client->shipping_country->iso_3166_2, $client->shipping_state);
} }

View File

@ -17,6 +17,7 @@ use App\Models\ClientContact;
use App\Models\ClientGatewayToken; use App\Models\ClientGatewayToken;
use App\Models\CompanyLedger; use App\Models\CompanyLedger;
use App\Models\Document; use App\Models\Document;
use App\Models\GroupSetting;
use App\Models\SystemLog; use App\Models\SystemLog;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use League\Fractal\Resource\Collection; use League\Fractal\Resource\Collection;
@ -42,6 +43,7 @@ class ClientTransformer extends EntityTransformer
'activities', 'activities',
'ledger', 'ledger',
'system_logs', 'system_logs',
'group_settings',
]; ];
/** /**
@ -96,6 +98,16 @@ class ClientTransformer extends EntityTransformer
return $this->includeCollection($client->system_logs, $transformer, SystemLog::class); return $this->includeCollection($client->system_logs, $transformer, SystemLog::class);
} }
public function includeGroupSettings(Client $client)
{
if (!$client->group_settings)
return null;
$transformer = new GroupSettingTransformer($this->serializer);
return $this->includeItem($client->group_settings, $transformer, GroupSetting::class);
}
/** /**
* @param Client $client * @param Client $client
* *

View File

@ -64,6 +64,7 @@ class UserTransformer extends EntityTransformer
'oauth_user_token' => empty($user->oauth_user_token) ? '' : '***', 'oauth_user_token' => empty($user->oauth_user_token) ? '' : '***',
'verified_phone_number' => (bool) $user->verified_phone_number, 'verified_phone_number' => (bool) $user->verified_phone_number,
'language_id' => (string) $user->language_id ?? '', 'language_id' => (string) $user->language_id ?? '',
'user_logged_in_notification' => (bool) $user->user_logged_in_notification,
]; ];
} }

View File

@ -15,8 +15,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.7.30'), 'app_version' => env('APP_VERSION','5.7.31'),
'app_tag' => env('APP_TAG','5.7.30'), 'app_tag' => env('APP_TAG','5.7.31'),
'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', ''), 'api_secret' => env('API_SECRET', ''),

View File

@ -0,0 +1,43 @@
<?php
use App\Models\Currency;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->boolean('user_logged_in_notification')->default(true);
});
$cur = Currency::find(120);
if(!$cur) {
$cur = new \App\Models\Currency();
$cur->id = 120;
$cur->code = 'TOP';
$cur->name = "Tongan Pa'anga";
$cur->symbol = 'T$';
$cur->thousand_separator = ',';
$cur->decimal_separator = '.';
$cur->precision = 2;
$cur->save();
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
//
}
};

View File

@ -142,6 +142,7 @@ class CurrenciesSeeder extends Seeder
['id' => 117, 'name' => 'Gold Troy Ounce', 'code' => 'XAU', 'symbol' => 'XAU', 'precision' => '3', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['id' => 117, 'name' => 'Gold Troy Ounce', 'code' => 'XAU', 'symbol' => 'XAU', 'precision' => '3', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 118, 'name' => 'Nicaraguan Córdoba', 'code' => 'NIO', 'symbol' => 'C$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['id' => 118, 'name' => 'Nicaraguan Córdoba', 'code' => 'NIO', 'symbol' => 'C$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 119, 'name' => 'Malagasy ariary', 'code' => 'MGA', 'symbol' => 'Ar', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => '.'], ['id' => 119, 'name' => 'Malagasy ariary', 'code' => 'MGA', 'symbol' => 'Ar', 'precision' => '0', 'thousand_separator' => ',', 'decimal_separator' => '.'],
['id' => 120, 'name' => "Tongan Pa anga", 'code' => 'TOP', 'symbol' => 'T$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
]; ];
foreach ($currencies as $currency) { foreach ($currencies as $currency) {

View File

@ -2402,6 +2402,9 @@ $LANG = array(
'currency_libyan_dinar' => 'Libyan Dinar', 'currency_libyan_dinar' => 'Libyan Dinar',
'currency_silver_troy_ounce' => 'Silver Troy Ounce', 'currency_silver_troy_ounce' => 'Silver Troy Ounce',
'currency_gold_troy_ounce' => 'Gold Troy Ounce', 'currency_gold_troy_ounce' => 'Gold Troy Ounce',
'currency_nicaraguan_córdoba' => 'Nicaraguan Córdoba',
'currency_malagasy_ariary' => 'Malagasy ariary',
"currency_tongan_pa_anga" => "Tongan Pa'anga",
'review_app_help' => 'We hope you\'re enjoying using the app.<br/>If you\'d consider :link we\'d greatly appreciate it!', 'review_app_help' => 'We hope you\'re enjoying using the app.<br/>If you\'d consider :link we\'d greatly appreciate it!',
'writing_a_review' => 'writing a review', 'writing_a_review' => 'writing a review',
@ -5180,6 +5183,8 @@ $LANG = array(
'upcoming' => 'Upcoming', 'upcoming' => 'Upcoming',
'client_contact' => 'Client Contact', 'client_contact' => 'Client Contact',
'uncategorized' => 'Uncategorized', 'uncategorized' => 'Uncategorized',
'login_notification' => 'Login Notification',
'login_notification_help' => 'Sends an email notifying that a login has taken place.'
); );
return $LANG; return $LANG;

View File

@ -64,7 +64,7 @@
{{ ctrans('texts.public_notes') }} {{ ctrans('texts.public_notes') }}
</dt> </dt>
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2"> <dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{{ $invoice->public_notes }} {!! html_entity_decode($invoice->public_notes) !!}
</dd> </dd>
@else @else
<dt class="text-sm font-medium leading-5 text-gray-500"> <dt class="text-sm font-medium leading-5 text-gray-500">

View File

@ -65,6 +65,34 @@ class ClientTest extends TestCase
$this->makeTestData(); $this->makeTestData();
} }
public function testStoreClientFixes()
{
$data = [
"contacts" => [
[
"email" => "tenda@gmail.com",
"first_name" => "Tenda",
"is_primary" => True,
"last_name" => "Bavuma",
"password" => null,
"send_email" => True
],
],
"country_id" => "356",
"display_name" => "Tenda Bavuma",
"name" => "Tenda Bavuma",
"shipping_country_id" => "356",
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/clients', $data);
$response->assertStatus(200);
}
public function testClientMergeContactDrop() public function testClientMergeContactDrop()
{ {

View File

@ -20,7 +20,6 @@ use App\Models\Account;
use App\Models\Company; use App\Models\Company;
use App\Models\Expense; use App\Models\Expense;
use App\Models\Invoice; use App\Models\Invoice;
use Tests\MockAccountData;
use App\Models\CompanyToken; use App\Models\CompanyToken;
use App\Models\ClientContact; use App\Models\ClientContact;
use App\Export\CSV\TaskExport; use App\Export\CSV\TaskExport;
@ -30,8 +29,6 @@ use App\Export\CSV\ProductExport;
use App\DataMapper\CompanySettings; use App\DataMapper\CompanySettings;
use App\Export\CSV\PaymentExport; use App\Export\CSV\PaymentExport;
use App\Factory\CompanyUserFactory; use App\Factory\CompanyUserFactory;
use App\Factory\InvoiceItemFactory;
use App\Services\Report\ARDetailReport;
use Illuminate\Routing\Middleware\ThrottleRequests; use Illuminate\Routing\Middleware\ThrottleRequests;
/** /**
@ -262,6 +259,21 @@ class ReportCsvGenerationTest extends TestCase
} }
public function testForcedInsertionOfMandatoryColumns()
{
$forced = ['client.name'];
$report_keys = ['invoice.number','client.name', 'invoice.amount'];
$array = array_merge($report_keys, array_diff($forced, $report_keys));
$this->assertEquals('client.name', $array[1]);
$report_keys = ['invoice.number','invoice.amount'];
$array = array_merge($report_keys, array_diff($forced, $report_keys));
$this->assertEquals('client.name', $array[2]);
}
public function testVendorCsvGeneration() public function testVendorCsvGeneration()
{ {
@ -322,7 +334,7 @@ class ReportCsvGenerationTest extends TestCase
$data = $export->returnJson(); $data = $export->returnJson();
$this->assertNotNull($data); $this->assertNotNull($data);
// nlog($data); // nlog($data);
// $this->assertEquals(0, $this->traverseJson($data, 'columns.0.identifier')); // $this->assertEquals(0, $this->traverseJson($data, 'columns.0.identifier'));
$this->assertEquals('Vendor Name', $this->traverseJson($data, 'columns.9.display_value')); $this->assertEquals('Vendor Name', $this->traverseJson($data, 'columns.9.display_value'));
$this->assertEquals('vendor', $this->traverseJson($data, '0.0.entity')); $this->assertEquals('vendor', $this->traverseJson($data, '0.0.entity'));
@ -1021,6 +1033,44 @@ class ReportCsvGenerationTest extends TestCase
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->post('/api/v1/reports/recurring_invoices', $data)->assertStatus(200); ])->post('/api/v1/reports/recurring_invoices', $data)->assertStatus(200);
}
public function testRecurringInvoiceColumnsCsvGeneration()
{
\App\Models\RecurringInvoice::factory()->create([
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'client_id' => $this->client->id,
'amount' => 100,
'balance' => 50,
'number' => '1234',
'status_id' => 2,
'discount' => 10,
'po_number' => '1234',
'public_notes' => 'Public',
'private_notes' => 'Private',
'terms' => 'Terms',
'frequency_id' => 1,
]);
$data = [
'date_range' => 'all',
'report_keys' => [],
'send_email' => false,
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/reports/recurring_invoices', $data);
$csv = $response->streamedContent();
$this->assertEquals('1234', $this->getFirstValueByColumn($csv, 'Recurring Invoice Invoice Number'));
$this->assertEquals('Daily', $this->getFirstValueByColumn($csv, 'Recurring Invoice How Often'));
$this->assertEquals('Active', $this->getFirstValueByColumn($csv, 'Recurring Invoice Status'));
} }

View File

@ -32,6 +32,8 @@ class InvoiceEmailTest extends TestCase
use DatabaseTransactions; use DatabaseTransactions;
use GeneratesCounter; use GeneratesCounter;
public $faker;
protected function setUp() :void protected function setUp() :void
{ {
parent::setUp(); parent::setUp();
@ -48,6 +50,14 @@ class InvoiceEmailTest extends TestCase
} }
public function testInvalidEmailParsing()
{
$email = 'illegal@example.com';
$this->assertTrue(strpos($email, '@example.com') !== false);
}
public function testClientEmailHistory() public function testClientEmailHistory()
{ {
$system_log = new SystemLog(); $system_log = new SystemLog();