diff --git a/VERSION.txt b/VERSION.txt index 25285daaef..557bab7d45 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.7.30 \ No newline at end of file +5.7.31 \ No newline at end of file diff --git a/app/Export/CSV/BaseExport.php b/app/Export/CSV/BaseExport.php index e034479289..fd1c1b5a3e 100644 --- a/app/Export/CSV/BaseExport.php +++ b/app/Export/CSV/BaseExport.php @@ -168,6 +168,7 @@ class BaseExport 'tax_rate1' => 'invoice.tax_rate1', 'tax_rate2' => 'invoice.tax_rate2', 'tax_rate3' => 'invoice.tax_rate3', + 'recurring_invoice' => 'invoice.recurring_id', ]; protected array $recurring_invoice_report_keys = [ @@ -230,7 +231,7 @@ class BaseExport 'po_number' => 'purchase_order.po_number', 'private_notes' => 'purchase_order.private_notes', 'public_notes' => 'purchase_order.public_notes', - 'status' => 'purchase_order.status_id', + 'status' => 'purchase_order.status', 'tax_name1' => 'purchase_order.tax_name1', 'tax_name2' => 'purchase_order.tax_name2', 'tax_name3' => 'purchase_order.tax_name3', @@ -377,6 +378,7 @@ class BaseExport "custom_value4" => "payment.custom_value4", "user" => "payment.user_id", "assigned_user" => "payment.assigned_user_id", + ]; protected array $expense_report_keys = [ @@ -429,6 +431,14 @@ class BaseExport 'project' => 'task.project_id', ]; + protected array $forced_client_fields = [ + "client.name", + ]; + + protected array $forced_vendor_fields = [ + "vendor.name", + ]; + protected function filterByClients($query) { if (isset($this->input['client_id']) && $this->input['client_id'] != 'all') { diff --git a/app/Export/CSV/CreditExport.php b/app/Export/CSV/CreditExport.php index 29afbfa161..c5c05f3c23 100644 --- a/app/Export/CSV/CreditExport.php +++ b/app/Export/CSV/CreditExport.php @@ -93,6 +93,8 @@ class CreditExport extends BaseExport $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() ->withTrashed() ->with('client') diff --git a/app/Export/CSV/InvoiceExport.php b/app/Export/CSV/InvoiceExport.php index 0a8f3188f2..4dc4ee3d8b 100644 --- a/app/Export/CSV/InvoiceExport.php +++ b/app/Export/CSV/InvoiceExport.php @@ -50,6 +50,8 @@ class InvoiceExport extends BaseExport $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() ->withTrashed() ->with('client') @@ -142,6 +144,11 @@ class InvoiceExport extends BaseExport 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; } diff --git a/app/Export/CSV/InvoiceItemExport.php b/app/Export/CSV/InvoiceItemExport.php index 74c1a64c25..5ac3c9fb2f 100644 --- a/app/Export/CSV/InvoiceItemExport.php +++ b/app/Export/CSV/InvoiceItemExport.php @@ -62,6 +62,8 @@ class InvoiceItemExport extends BaseExport $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() ->withTrashed() ->with('client') @@ -200,6 +202,27 @@ class InvoiceItemExport extends BaseExport $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; } diff --git a/app/Export/CSV/PaymentExport.php b/app/Export/CSV/PaymentExport.php index 01c8641b07..7f77563f9b 100644 --- a/app/Export/CSV/PaymentExport.php +++ b/app/Export/CSV/PaymentExport.php @@ -48,6 +48,8 @@ class PaymentExport extends BaseExport $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() ->withTrashed() ->where('company_id', $this->company->id) @@ -69,6 +71,8 @@ class PaymentExport extends BaseExport return ['identifier' => $key, 'display_value' => $headerdisplay[$value]]; })->toArray(); + nlog($header); + $report = $query->cursor() ->map(function ($resource) { $row = $this->buildRow($resource); diff --git a/app/Export/CSV/PurchaseOrderExport.php b/app/Export/CSV/PurchaseOrderExport.php index 88145822b1..45e8a6adb1 100644 --- a/app/Export/CSV/PurchaseOrderExport.php +++ b/app/Export/CSV/PurchaseOrderExport.php @@ -54,7 +54,7 @@ class PurchaseOrderExport extends BaseExport 'po_number' => 'purchase_order.po_number', 'private_notes' => 'purchase_order.private_notes', 'public_notes' => 'purchase_order.public_notes', - 'status' => 'purchase_order.status_id', + 'status' => 'purchase_order.status', 'tax_name1' => 'purchase_order.tax_name1', 'tax_name2' => 'purchase_order.tax_name2', 'tax_name3' => 'purchase_order.tax_name3', @@ -95,6 +95,9 @@ class PurchaseOrderExport extends BaseExport if (count($this->input['report_keys']) == 0) { $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() ->withTrashed() ->with('vendor') @@ -181,8 +184,8 @@ class PurchaseOrderExport extends BaseExport $entity['vendor'] = $purchase_order->vendor->present()->name(); } - if (in_array('status_id', $this->input['report_keys'])) { - $entity['status'] = $purchase_order->stringStatus($purchase_order->status_id); + if (in_array('purchase_order.status', $this->input['report_keys'])) { + $entity['purchase_order.status'] = $purchase_order->stringStatus($purchase_order->status_id); } return $entity; diff --git a/app/Export/CSV/PurchaseOrderItemExport.php b/app/Export/CSV/PurchaseOrderItemExport.php index 9ebf2991c9..fb09ea6fa8 100644 --- a/app/Export/CSV/PurchaseOrderItemExport.php +++ b/app/Export/CSV/PurchaseOrderItemExport.php @@ -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_merge($this->input['report_keys'], array_diff($this->forced_vendor_fields, $this->input['report_keys'])); + $query = PurchaseOrder::query() ->withTrashed() ->with('vendor')->where('company_id', $this->company->id) diff --git a/app/Export/CSV/QuoteExport.php b/app/Export/CSV/QuoteExport.php index 8083c0a71e..eba6adbcef 100644 --- a/app/Export/CSV/QuoteExport.php +++ b/app/Export/CSV/QuoteExport.php @@ -56,6 +56,8 @@ class QuoteExport extends BaseExport $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() ->withTrashed() ->with('client') diff --git a/app/Export/CSV/QuoteItemExport.php b/app/Export/CSV/QuoteItemExport.php index f85a82dd4c..48bc25b95e 100644 --- a/app/Export/CSV/QuoteItemExport.php +++ b/app/Export/CSV/QuoteItemExport.php @@ -57,6 +57,8 @@ class QuoteItemExport extends BaseExport $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() ->withTrashed() ->with('client')->where('company_id', $this->company->id) diff --git a/app/Export/CSV/RecurringInvoiceExport.php b/app/Export/CSV/RecurringInvoiceExport.php index d30359510c..3234b06743 100644 --- a/app/Export/CSV/RecurringInvoiceExport.php +++ b/app/Export/CSV/RecurringInvoiceExport.php @@ -48,6 +48,8 @@ class RecurringInvoiceExport extends BaseExport $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() ->withTrashed() ->with('client') @@ -135,8 +137,8 @@ class RecurringInvoiceExport extends BaseExport $entity['client'] = $invoice->client->present()->name(); } - if (in_array('status_id', $this->input['report_keys'])) { - $entity['status'] = $invoice->stringStatus($invoice->status_id); + if (in_array('recurring_invoice.status', $this->input['report_keys'])) { + $entity['recurring_invoice.status'] = $invoice->stringStatus($invoice->status_id); } if (in_array('project_id', $this->input['report_keys'])) { diff --git a/app/Export/CSV/TaskExport.php b/app/Export/CSV/TaskExport.php index 87834c6aee..b2eb6425c5 100644 --- a/app/Export/CSV/TaskExport.php +++ b/app/Export/CSV/TaskExport.php @@ -60,6 +60,8 @@ class TaskExport extends BaseExport $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() ->withTrashed() ->where('company_id', $this->company->id) diff --git a/app/Factory/UserFactory.php b/app/Factory/UserFactory.php index d31a0019b2..9b15037f62 100644 --- a/app/Factory/UserFactory.php +++ b/app/Factory/UserFactory.php @@ -27,7 +27,8 @@ class UserFactory $user->last_login = now(); $user->failed_logins = 0; $user->signature = ''; - $user->theme_id = 0; + $user->theme_id = 0; + $user->user_logged_in_notification = true; return $user; } diff --git a/app/Helpers/Epc/EpcQrGenerator.php b/app/Helpers/Epc/EpcQrGenerator.php index 62d61a0a15..342c5f775f 100644 --- a/app/Helpers/Epc/EpcQrGenerator.php +++ b/app/Helpers/Epc/EpcQrGenerator.php @@ -50,7 +50,8 @@ class EpcQrGenerator ); $writer = new Writer($renderer); - $this->validateFields(); + if($this->validateFields()) + return ''; $qr = $writer->writeString($this->encodeMessage(), 'utf-8'); @@ -87,12 +88,16 @@ class EpcQrGenerator private function validateFields() { 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'); } if (Ninja::isSelfHost() && isset($this->company?->custom_fields?->company1)) { + return true; nlog('The IBAN field is required'); } + + return false; } private function formatMoney($value) diff --git a/app/Helpers/SwissQr/SwissQrGenerator.php b/app/Helpers/SwissQr/SwissQrGenerator.php index abacbb57c0..d8c1ad7730 100644 --- a/app/Helpers/SwissQr/SwissQrGenerator.php +++ b/app/Helpers/SwissQr/SwissQrGenerator.php @@ -174,9 +174,14 @@ class SwissQrGenerator return $html; } catch (\Exception $e) { - foreach ($qrBill->getViolations() as $key => $violation) { - nlog("qr"); - nlog($violation); + + if(is_iterable($qrBill->getViolations())) { + + foreach ($qrBill->getViolations() as $key => $violation) { + nlog("qr"); + nlog($violation); + } + } nlog($e->getMessage()); diff --git a/app/Http/Controllers/EmailHistoryController.php b/app/Http/Controllers/EmailHistoryController.php index 4da69ba7b6..aea6a5e200 100644 --- a/app/Http/Controllers/EmailHistoryController.php +++ b/app/Http/Controllers/EmailHistoryController.php @@ -31,11 +31,11 @@ class EmailHistoryController extends BaseController ->where('category_id', SystemLog::CATEGORY_MAIL) ->orderBy('id', 'DESC') ->cursor() - ->map(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']; - } - }); + ->filter(function ($system_log) { + return ($system_log->log['history'] && isset($system_log->log['history']['events']) && count($system_log->log['history']['events']) >=1) !== false; + })->map(function ($system_log) { + return $system_log->log['history']; + })->values()->all(); return response()->json($data, 200); @@ -51,16 +51,17 @@ class EmailHistoryController extends BaseController /** @var \App\Models\User $user */ $user = auth()->user(); + $data = SystemLog::where('company_id', $user->company()->id) - ->where('category_id', SystemLog::CATEGORY_MAIL) - ->whereJsonContains('log->history->entity_id', $this->encodePrimaryKey($request->entity_id)) - ->orderBy('id', 'DESC') - ->cursor() - ->map(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']; - } - }); + ->where('category_id', SystemLog::CATEGORY_MAIL) + ->whereJsonContains('log->history->entity_id', $this->encodePrimaryKey($request->entity_id)) + ->orderBy('id', 'DESC') + ->cursor() + ->filter(function ($system_log) { + return ($system_log->log['history'] && isset($system_log->log['history']['events']) && count($system_log->log['history']['events']) >=1) !== false; + })->map(function ($system_log) { + return $system_log->log['history']; + })->values()->all(); return response()->json($data, 200); diff --git a/app/Http/Controllers/RecurringInvoiceController.php b/app/Http/Controllers/RecurringInvoiceController.php index d09d31e802..94c2192d17 100644 --- a/app/Http/Controllers/RecurringInvoiceController.php +++ b/app/Http/Controllers/RecurringInvoiceController.php @@ -157,6 +157,7 @@ class RecurringInvoiceController extends BaseController $user = auth()->user(); $recurring_invoice = RecurringInvoiceFactory::create($user->company()->id, $user->id); + $recurring_invoice->auto_bill = $user->company()->settings->auto_bill; return $this->itemResponse($recurring_invoice); } diff --git a/app/Http/Livewire/TasksTable.php b/app/Http/Livewire/TasksTable.php index 40106c390b..f229db889f 100644 --- a/app/Http/Livewire/TasksTable.php +++ b/app/Http/Livewire/TasksTable.php @@ -39,11 +39,11 @@ class TasksTable extends Component ->where('is_deleted', false) ->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'); } - 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'); } diff --git a/app/Http/Requests/Request.php b/app/Http/Requests/Request.php index 2fb9a589e0..8d7eb2acd0 100644 --- a/app/Http/Requests/Request.php +++ b/app/Http/Requests/Request.php @@ -180,7 +180,7 @@ class Request extends FormRequest } //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) { $input['contacts'][$key]['password'] = ''; } else { diff --git a/app/Jobs/Mail/NinjaMailerObject.php b/app/Jobs/Mail/NinjaMailerObject.php index 1d973cb902..df33c35c2a 100644 --- a/app/Jobs/Mail/NinjaMailerObject.php +++ b/app/Jobs/Mail/NinjaMailerObject.php @@ -32,12 +32,12 @@ class NinjaMailerObject /* Variable for cascading notifications */ 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 $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 $reminder_template = ''; diff --git a/app/Listeners/User/UpdateUserLastLogin.php b/app/Listeners/User/UpdateUserLastLogin.php index 9fc383db20..94e20512ee 100644 --- a/app/Listeners/User/UpdateUserLastLogin.php +++ b/app/Listeners/User/UpdateUserLastLogin.php @@ -55,7 +55,7 @@ class UpdateUserLastLogin implements ShouldQueue $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->mailable = new UserLoggedIn($user, $user->account->companies->first(), $ip); $nmo->company = $user->account->companies->first(); @@ -69,6 +69,7 @@ class UpdateUserLastLogin implements ShouldQueue Cache::put($key, true, 60 * 24); $arr = json_encode(['ip' => $ip]); + $arr = ctrans('texts.new_login_detected'). " {$ip}"; SystemLogger::dispatch( $arr, diff --git a/app/Mail/TemplateEmail.php b/app/Mail/TemplateEmail.php index 984029cefe..63794936e5 100644 --- a/app/Mail/TemplateEmail.php +++ b/app/Mail/TemplateEmail.php @@ -108,12 +108,15 @@ class TemplateEmail extends Mailable if (strlen($settings->bcc_email) > 1) { if (Ninja::isHosted()) { - $bccs = explode(',', str_replace(' ', '', $settings->bcc_email)); - $this->bcc(array_slice($bccs, 0, 2)); - //$this->bcc(reset($bccs)); //remove whitespace if any has been inserted. + + if($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))); - }//remove whitespace if any has been inserted. + } } $this->subject(str_replace("
", "", $this->build_email->getSubject())) diff --git a/app/Mail/VendorTemplateEmail.php b/app/Mail/VendorTemplateEmail.php index 2ac0987b80..bdfb818b00 100644 --- a/app/Mail/VendorTemplateEmail.php +++ b/app/Mail/VendorTemplateEmail.php @@ -11,10 +11,11 @@ namespace App\Mail; +use App\Utils\Ninja; use App\Models\VendorContact; -use App\Services\PdfMaker\Designs\Utilities\DesignHelpers; -use App\Utils\VendorHtmlEngine; use Illuminate\Mail\Mailable; +use App\Utils\VendorHtmlEngine; +use App\Services\PdfMaker\Designs\Utilities\DesignHelpers; class VendorTemplateEmail extends Mailable { @@ -102,8 +103,19 @@ class VendorTemplateEmail extends Mailable $this->from(config('mail.from.address'), $email_from_name); 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()) ->text('email.template.text', [ diff --git a/app/Models/Presenters/ClientPresenter.php b/app/Models/Presenters/ClientPresenter.php index ac4b184d94..aed92c0eb8 100644 --- a/app/Models/Presenters/ClientPresenter.php +++ b/app/Models/Presenters/ClientPresenter.php @@ -23,7 +23,7 @@ class ClientPresenter extends EntityPresenter */ public function name() { - if ($this->entity->name) { + if (strlen($this->entity->name) > 1) { return $this->entity->name; } diff --git a/app/Models/User.php b/app/Models/User.php index 22e5130d0d..aec9b0e787 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -73,7 +73,8 @@ use Illuminate\Foundation\Auth\User as Authenticatable; * @property int|null $deleted_at * @property string|null $oauth_user_refresh_token * @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 string|null $sms_verification_code * @property bool $verified_phone_number @@ -140,6 +141,7 @@ class User extends Authenticatable implements MustVerifyEmail * */ protected $fillable = [ + 'user_logged_in_notification', 'first_name', 'last_name', 'email', diff --git a/app/PaymentDrivers/Eway/CreditCard.php b/app/PaymentDrivers/Eway/CreditCard.php index 11dbafdcfe..d4203d0eab 100644 --- a/app/PaymentDrivers/Eway/CreditCard.php +++ b/app/PaymentDrivers/Eway/CreditCard.php @@ -251,7 +251,7 @@ class CreditCard $response = $this->eway_driver->init()->eway->createTransaction(\Eway\Rapid\Enum\ApiMethod::DIRECT, $transaction); - if ($response->TransactionStatus) { + if ($response->TransactionStatus ?? false) { $this->logResponse($response, true); $payment = $this->storePayment($response); } else { diff --git a/app/PaymentDrivers/Square/SquareWebhook.php b/app/PaymentDrivers/Square/SquareWebhook.php index c83b73fd47..67d656e8c9 100644 --- a/app/PaymentDrivers/Square/SquareWebhook.php +++ b/app/PaymentDrivers/Square/SquareWebhook.php @@ -134,8 +134,8 @@ class SquareWebhook implements ShouldQueue nlog("Searching by payment hash"); - $payment_hash_id = $apiResponse->getPayment()->getReferenceId() ?? false; - $square_payment = $apiResponse->getPayment()->jsonSerialize(); + $payment_hash_id = $apiResponse->getResult()->getPayment()->getReferenceId() ?? false; + $square_payment = $apiResponse->getResult()->getPayment()->jsonSerialize(); $payment_hash = PaymentHash::query()->where('hash', $payment_hash_id)->firstOrFail(); $payment_hash->data = array_merge((array) $payment_hash->data, (array)$square_payment); diff --git a/app/Services/Email/Email.php b/app/Services/Email/Email.php index cebb8b7b6f..c539d075d4 100644 --- a/app/Services/Email/Email.php +++ b/app/Services/Email/Email.php @@ -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 ? 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; @@ -413,6 +413,14 @@ class Email implements ShouldQueue if ($address_object->address == " ") { return true; } + + if ($address_object->address == "") { + return true; + } + + if($address_object->name == " " || $address_object->name == "") { + return true; + } } diff --git a/app/Services/Email/EmailDefaults.php b/app/Services/Email/EmailDefaults.php index 401060b6c7..1f78facaa1 100644 --- a/app/Services/Email/EmailDefaults.php +++ b/app/Services/Email/EmailDefaults.php @@ -255,8 +255,8 @@ class EmailDefaults if (strlen($this->email->email_object->settings->bcc_email) > 1) { if (Ninja::isHosted() && $this->email->company->account->isPaid()) { - $bccs = array_slice(explode(',', str_replace(' ', '', $this->email->email_object->settings->bcc_email)), 0, 2); - } elseif (Ninja::isSelfHost()) { + $bccs = array_slice(explode(',', str_replace(' ', '', $this->email->email_object->settings->bcc_email)), 0, 5); + } else { $bccs = (explode(',', str_replace(' ', '', $this->email->email_object->settings->bcc_email))); } } diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php index 0585f888bd..19a366fdc5 100644 --- a/app/Services/Invoice/AutoBillInvoice.php +++ b/app/Services/Invoice/AutoBillInvoice.php @@ -120,7 +120,7 @@ class AutoBillInvoice extends AbstractService /* Build payment hash */ $payment_hash = PaymentHash::create([ - 'hash' => Str::random(64), + 'hash' => Str::random(32), 'data' => [ 'amount_with_fee' => $amount + $fee, 'invoices' => [ diff --git a/app/Services/Invoice/EInvoice/ZugferdEInvoice.php b/app/Services/Invoice/EInvoice/ZugferdEInvoice.php index 6b7dbe8843..539e78f6e2 100644 --- a/app/Services/Invoice/EInvoice/ZugferdEInvoice.php +++ b/app/Services/Invoice/EInvoice/ZugferdEInvoice.php @@ -75,7 +75,7 @@ class ZugferdEInvoice extends AbstractService } else { $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); } diff --git a/app/Transformers/ClientTransformer.php b/app/Transformers/ClientTransformer.php index 711c9cfec5..4cfb7742b3 100644 --- a/app/Transformers/ClientTransformer.php +++ b/app/Transformers/ClientTransformer.php @@ -17,6 +17,7 @@ use App\Models\ClientContact; use App\Models\ClientGatewayToken; use App\Models\CompanyLedger; use App\Models\Document; +use App\Models\GroupSetting; use App\Models\SystemLog; use App\Utils\Traits\MakesHash; use League\Fractal\Resource\Collection; @@ -42,6 +43,7 @@ class ClientTransformer extends EntityTransformer 'activities', 'ledger', 'system_logs', + 'group_settings', ]; /** @@ -96,6 +98,16 @@ class ClientTransformer extends EntityTransformer 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 * diff --git a/app/Transformers/UserTransformer.php b/app/Transformers/UserTransformer.php index ffe029fbb0..0971c7dc2b 100644 --- a/app/Transformers/UserTransformer.php +++ b/app/Transformers/UserTransformer.php @@ -64,6 +64,7 @@ class UserTransformer extends EntityTransformer 'oauth_user_token' => empty($user->oauth_user_token) ? '' : '***', 'verified_phone_number' => (bool) $user->verified_phone_number, 'language_id' => (string) $user->language_id ?? '', + 'user_logged_in_notification' => (bool) $user->user_logged_in_notification, ]; } diff --git a/config/ninja.php b/config/ninja.php index 7ef548ab7e..df657179e4 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -15,8 +15,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => env('APP_VERSION','5.7.30'), - 'app_tag' => env('APP_TAG','5.7.30'), + 'app_version' => env('APP_VERSION','5.7.31'), + 'app_tag' => env('APP_TAG','5.7.31'), 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', ''), diff --git a/database/migrations/2023_10_18_061415_add_user_notification_suppression.php b/database/migrations/2023_10_18_061415_add_user_notification_suppression.php new file mode 100644 index 0000000000..4c01624a02 --- /dev/null +++ b/database/migrations/2023_10_18_061415_add_user_notification_suppression.php @@ -0,0 +1,43 @@ +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 + { + // + } +}; diff --git a/database/seeders/CurrenciesSeeder.php b/database/seeders/CurrenciesSeeder.php index b2c8b35af7..e6976622ee 100644 --- a/database/seeders/CurrenciesSeeder.php +++ b/database/seeders/CurrenciesSeeder.php @@ -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' => 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' => 120, 'name' => "Tongan Pa anga", 'code' => 'TOP', 'symbol' => 'T$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'], ]; foreach ($currencies as $currency) { diff --git a/lang/en/texts.php b/lang/en/texts.php index 5fe2741906..48cfe30beb 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -2402,6 +2402,9 @@ $LANG = array( 'currency_libyan_dinar' => 'Libyan Dinar', 'currency_silver_troy_ounce' => 'Silver 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.
If you\'d consider :link we\'d greatly appreciate it!', 'writing_a_review' => 'writing a review', @@ -5180,6 +5183,8 @@ $LANG = array( 'upcoming' => 'Upcoming', 'client_contact' => 'Client Contact', 'uncategorized' => 'Uncategorized', + 'login_notification' => 'Login Notification', + 'login_notification_help' => 'Sends an email notifying that a login has taken place.' ); return $LANG; diff --git a/resources/views/portal/ninja2020/invoices/payment.blade.php b/resources/views/portal/ninja2020/invoices/payment.blade.php index 8d5df833d3..a0d786ff46 100644 --- a/resources/views/portal/ninja2020/invoices/payment.blade.php +++ b/resources/views/portal/ninja2020/invoices/payment.blade.php @@ -64,7 +64,7 @@ {{ ctrans('texts.public_notes') }}
- {{ $invoice->public_notes }} + {!! html_entity_decode($invoice->public_notes) !!}
@else
diff --git a/tests/Feature/ClientTest.php b/tests/Feature/ClientTest.php index 0244a3e822..89b49d945c 100644 --- a/tests/Feature/ClientTest.php +++ b/tests/Feature/ClientTest.php @@ -65,6 +65,34 @@ class ClientTest extends TestCase $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() { diff --git a/tests/Feature/Export/ReportCsvGenerationTest.php b/tests/Feature/Export/ReportCsvGenerationTest.php index c7f400db74..87c967c883 100644 --- a/tests/Feature/Export/ReportCsvGenerationTest.php +++ b/tests/Feature/Export/ReportCsvGenerationTest.php @@ -20,7 +20,6 @@ use App\Models\Account; use App\Models\Company; use App\Models\Expense; use App\Models\Invoice; -use Tests\MockAccountData; use App\Models\CompanyToken; use App\Models\ClientContact; use App\Export\CSV\TaskExport; @@ -30,8 +29,6 @@ use App\Export\CSV\ProductExport; use App\DataMapper\CompanySettings; use App\Export\CSV\PaymentExport; use App\Factory\CompanyUserFactory; -use App\Factory\InvoiceItemFactory; -use App\Services\Report\ARDetailReport; 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() { @@ -322,7 +334,7 @@ class ReportCsvGenerationTest extends TestCase $data = $export->returnJson(); $this->assertNotNull($data); -// nlog($data); + // nlog($data); // $this->assertEquals(0, $this->traverseJson($data, 'columns.0.identifier')); $this->assertEquals('Vendor Name', $this->traverseJson($data, 'columns.9.display_value')); $this->assertEquals('vendor', $this->traverseJson($data, '0.0.entity')); @@ -1021,6 +1033,44 @@ class ReportCsvGenerationTest extends TestCase 'X-API-TOKEN' => $this->token, ])->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')); } diff --git a/tests/Feature/InvoiceEmailTest.php b/tests/Feature/InvoiceEmailTest.php index a08b306aa4..23ba032360 100644 --- a/tests/Feature/InvoiceEmailTest.php +++ b/tests/Feature/InvoiceEmailTest.php @@ -32,6 +32,8 @@ class InvoiceEmailTest extends TestCase use DatabaseTransactions; use GeneratesCounter; + public $faker; + protected function setUp() :void { 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() { $system_log = new SystemLog();