diff --git a/VERSION.txt b/VERSION.txt index b2198ea2611b..faea5e9484eb 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.5.43 \ No newline at end of file +5.5.44 \ No newline at end of file diff --git a/app/Export/CSV/RecurringInvoiceExport.php b/app/Export/CSV/RecurringInvoiceExport.php index 30accbd22511..1bacb9d6a301 100644 --- a/app/Export/CSV/RecurringInvoiceExport.php +++ b/app/Export/CSV/RecurringInvoiceExport.php @@ -54,6 +54,7 @@ class RecurringInvoiceExport extends BaseExport 'po_number' => 'po_number', 'private_notes' => 'private_notes', 'public_notes' => 'public_notes', + 'next_send_date' => 'next_send_date', 'status' => 'status_id', 'tax_name1' => 'tax_name1', 'tax_name2' => 'tax_name2', @@ -66,6 +67,7 @@ class RecurringInvoiceExport extends BaseExport 'currency' => 'currency_id', 'vendor' => 'vendor_id', 'project' => 'project_id', + 'frequency' => 'frequency_id' ]; private array $decorate_keys = [ @@ -162,6 +164,8 @@ class RecurringInvoiceExport extends BaseExport $entity['vendor'] = $invoice->vendor ? $invoice->vendor->name : ''; } + $entity['frequency'] = $invoice->frequencyForKey($invoice->frequency_id); + return $entity; } } diff --git a/app/Filters/DocumentFilters.php b/app/Filters/DocumentFilters.php index fade4950705a..bf7b589aeef0 100644 --- a/app/Filters/DocumentFilters.php +++ b/app/Filters/DocumentFilters.php @@ -11,6 +11,7 @@ namespace App\Filters; +use App\Models\Company; use App\Models\User; use Illuminate\Database\Eloquent\Builder; @@ -54,6 +55,15 @@ class DocumentFilters extends QueryFilters return $this->builder->orderBy($sort_col[0], $sort_col[1]); } + + public function company_documents($value = 'false') + { + if($value == 'true') + return $this->builder->where('documentable_type', Company::class); + + return $this->builder; + } + /** * Returns the base query. * diff --git a/app/Http/Controllers/EmailController.php b/app/Http/Controllers/EmailController.php index 17b3f8ff0a78..7c2e75966b91 100644 --- a/app/Http/Controllers/EmailController.php +++ b/app/Http/Controllers/EmailController.php @@ -131,8 +131,6 @@ class EmailController extends BaseController if(Ninja::isHosted() && !$entity_obj->company->account->account_sms_verified) return response(['message' => 'Please verify your account to send emails.'], 400); - nlog($entity); - if($entity == 'purchaseOrder' || $entity == 'purchase_order' || $template == 'purchase_order' || $entity == 'App\Models\PurchaseOrder'){ return $this->sendPurchaseOrder($entity_obj, $data, $template); } @@ -141,7 +139,7 @@ class EmailController extends BaseController if (! $invitation->contact->trashed() && $invitation->contact->email) { $entity_obj->service()->markSent()->save(); - EmailEntity::dispatch($invitation->fresh(), $invitation->company, $template, $data); + EmailEntity::dispatch($invitation->fresh(), $invitation->company, $template, $data)->delay(now()->addSeconds(2)); } }); @@ -194,7 +192,7 @@ class EmailController extends BaseController $data['template'] = $template; - PurchaseOrderEmail::dispatch($entity_obj, $entity_obj->company, $data); + PurchaseOrderEmail::dispatch($entity_obj, $entity_obj->company, $data)->delay(now()->addSeconds(2)); return $this->itemResponse($entity_obj); diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index 25cc858e7500..41102869a90e 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -136,6 +136,8 @@ class ImportController extends Controller } $csv = Reader::createFromString($csvfile); + $csvdelimiter = self::detectDelimiter($csvfile); + $csv->setDelimiter($csvdelimiter); $stmt = new Statement(); $data = iterator_to_array($stmt->process($csv)); @@ -156,4 +158,17 @@ class ImportController extends Controller return $data; } + + public function detectDelimiter($csvfile) + { + $delimiters = array(',', '.', ';'); + $bestDelimiter = false; + $count = 0; + foreach ($delimiters as $delimiter) + if (substr_count($csvfile, $delimiter) > $count) { + $count = substr_count($csvfile, $delimiter); + $bestDelimiter = $delimiter; + } + return $bestDelimiter; + } } diff --git a/app/Http/Controllers/OpenAPI/VendorSchema.php b/app/Http/Controllers/OpenAPI/VendorSchema.php index 8cf71c4ca9ea..a50df21d6f07 100644 --- a/app/Http/Controllers/OpenAPI/VendorSchema.php +++ b/app/Http/Controllers/OpenAPI/VendorSchema.php @@ -26,7 +26,7 @@ * @OA\Property(property="city", type="string", example="", description="________"), * @OA\Property(property="state", type="string", example="", description="________"), * @OA\Property(property="postal_code", type="string", example="", description="________"), - * @OA\Property(property="work_phone", type="string", example="555-3434-3434", description="The client phone number"), + * @OA\Property(property="phone", type="string", example="555-3434-3434", description="The client phone number"), * @OA\Property(property="country_id", type="string", example="", description="________"), * @OA\Property(property="currency_id", type="string", example="4", description="________"), * @OA\Property(property="custom_value1", type="string", example="", description="________"), diff --git a/app/Http/Requests/BankIntegration/StoreBankIntegrationRequest.php b/app/Http/Requests/BankIntegration/StoreBankIntegrationRequest.php index 933f5848624d..80b53208a712 100644 --- a/app/Http/Requests/BankIntegration/StoreBankIntegrationRequest.php +++ b/app/Http/Requests/BankIntegration/StoreBankIntegrationRequest.php @@ -44,7 +44,7 @@ class StoreBankIntegrationRequest extends Request { $input = $this->all(); - if(!array_key_exists('provider_name', $input) || strlen($input['provider_name']) == 0 && array_key_exists('bank_account_name', $input)) + if((!array_key_exists('provider_name', $input) || strlen($input['provider_name']) == 0) && array_key_exists('bank_account_name', $input)) $input['provider_name'] = $input['bank_account_name']; $this->replace($input); diff --git a/app/Http/Requests/BankTransaction/StoreBankTransactionRequest.php b/app/Http/Requests/BankTransaction/StoreBankTransactionRequest.php index 529eaa79a61a..c2e6fe68bde9 100644 --- a/app/Http/Requests/BankTransaction/StoreBankTransactionRequest.php +++ b/app/Http/Requests/BankTransaction/StoreBankTransactionRequest.php @@ -34,8 +34,7 @@ class StoreBankTransactionRequest extends Request $rules = []; - if(isset($this->bank_integration_id)) - $rules['bank_integration_id'] = 'bail|required|exists:bank_integrations,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; + $rules['bank_integration_id'] = 'bail|required|exists:bank_integrations,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; return $rules; } @@ -44,7 +43,9 @@ class StoreBankTransactionRequest extends Request { $input = $this->all(); - if(array_key_exists('bank_integration_id', $input) && strlen($input['bank_integration_id']) > 1 && !is_numeric($input['bank_integration_id'])) + if(array_key_exists('bank_integration_id', $input) && $input['bank_integration_id'] == "") + unset($input['bank_integration_id']); + elseif(array_key_exists('bank_integration_id', $input) && strlen($input['bank_integration_id']) > 1 && !is_numeric($input['bank_integration_id'])) $input['bank_integration_id'] = $this->decodePrimaryKey($input['bank_integration_id']); $this->replace($input); diff --git a/app/Http/Requests/BankTransaction/UpdateBankTransactionRequest.php b/app/Http/Requests/BankTransaction/UpdateBankTransactionRequest.php index 4f251c916766..54f789714316 100644 --- a/app/Http/Requests/BankTransaction/UpdateBankTransactionRequest.php +++ b/app/Http/Requests/BankTransaction/UpdateBankTransactionRequest.php @@ -45,8 +45,7 @@ class UpdateBankTransactionRequest extends Request if(isset($this->expense_id)) $rules['expense_id'] = 'bail|required|exists:expenses,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; - if(isset($this->bank_integration_id)) - $rules['bank_integration_id'] = 'bail|required|exists:bank_integrations,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; + $rules['bank_integration_id'] = 'bail|required|exists:bank_integrations,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; return $rules; @@ -69,7 +68,9 @@ class UpdateBankTransactionRequest extends Request if(array_key_exists('ninja_category_id', $input) && strlen($input['ninja_category_id']) > 1) $input['ninja_category_id'] = $this->decodePrimaryKey($input['ninja_category_id']); - if(array_key_exists('bank_integration_id', $input) && strlen($input['bank_integration_id']) > 1) + if(array_key_exists('bank_integration_id', $input) && $input['bank_integration_id'] == "") + unset($input['bank_integration_id']); + elseif(array_key_exists('bank_integration_id', $input) && strlen($input['bank_integration_id']) > 1) $input['bank_integration_id'] = $this->decodePrimaryKey($input['bank_integration_id']); $this->replace($input); diff --git a/app/Http/Requests/Expense/StoreExpenseRequest.php b/app/Http/Requests/Expense/StoreExpenseRequest.php index 0c964fef9016..a36369f01e90 100644 --- a/app/Http/Requests/Expense/StoreExpenseRequest.php +++ b/app/Http/Requests/Expense/StoreExpenseRequest.php @@ -45,6 +45,8 @@ class StoreExpenseRequest extends Request $rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id; } + $rules['category_id'] = 'bail|nullable|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; + return $this->globalRules($rules); } @@ -54,10 +56,6 @@ class StoreExpenseRequest extends Request $input = $this->decodePrimaryKeys($input); - if (array_key_exists('category_id', $input) && is_string($input['category_id'])) { - $input['category_id'] = $this->decodePrimaryKey($input['category_id']); - } - if (! array_key_exists('currency_id', $input) || strlen($input['currency_id']) == 0) { $input['currency_id'] = (string) auth()->user()->company()->settings->currency_id; } @@ -66,7 +64,6 @@ class StoreExpenseRequest extends Request $input['color'] = ''; } - /* Ensure the project is related */ if (array_key_exists('project_id', $input) && isset($input['project_id'])) { $project = Project::withTrashed()->where('id', $input['project_id'])->company()->first(); diff --git a/app/Http/Requests/Expense/UpdateExpenseRequest.php b/app/Http/Requests/Expense/UpdateExpenseRequest.php index 26b144731048..348cad69ddba 100644 --- a/app/Http/Requests/Expense/UpdateExpenseRequest.php +++ b/app/Http/Requests/Expense/UpdateExpenseRequest.php @@ -41,6 +41,8 @@ class UpdateExpenseRequest extends Request $rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id)->ignore($this->expense->id); } + $rules['category_id'] = 'bail|sometimes|nullable|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; + return $this->globalRules($rules); } @@ -50,10 +52,6 @@ class UpdateExpenseRequest extends Request $input = $this->decodePrimaryKeys($input); - if (array_key_exists('category_id', $input) && is_string($input['category_id'])) { - $input['category_id'] = $this->decodePrimaryKey($input['category_id']); - } - if (array_key_exists('documents', $input)) { unset($input['documents']); } diff --git a/app/Http/ValidationRules/Company/ValidCompanyQuantity.php b/app/Http/ValidationRules/Company/ValidCompanyQuantity.php index a90e861c9ae2..fa04edd4a721 100644 --- a/app/Http/ValidationRules/Company/ValidCompanyQuantity.php +++ b/app/Http/ValidationRules/Company/ValidCompanyQuantity.php @@ -39,6 +39,6 @@ class ValidCompanyQuantity implements Rule */ public function message() { - return ctrans('texts.company_limit_reached'); + return ctrans('texts.company_limit_reached', ['limit' => Ninja::isSelfHost() ? 10 : auth()->user()->company()->account->hosted_company_count]); } } diff --git a/app/Import/Transformer/Csv/ClientTransformer.php b/app/Import/Transformer/Csv/ClientTransformer.php index a2c4721bb8eb..543ad1cd205a 100644 --- a/app/Import/Transformer/Csv/ClientTransformer.php +++ b/app/Import/Transformer/Csv/ClientTransformer.php @@ -38,7 +38,7 @@ class ClientTransformer extends BaseTransformer return [ 'company_id' => $this->company->id, 'name' => $this->getString($data, 'client.name'), - 'work_phone' => $this->getString($data, 'client.phone'), + 'phone' => $this->getString($data, 'client.phone'), 'address1' => $this->getString($data, 'client.address1'), 'address2' => $this->getString($data, 'client.address2'), 'postal_code' => $this->getString($data, 'client.postal_code'), diff --git a/app/Import/Transformer/Wave/ClientTransformer.php b/app/Import/Transformer/Wave/ClientTransformer.php index c5681e411152..844560f48679 100644 --- a/app/Import/Transformer/Wave/ClientTransformer.php +++ b/app/Import/Transformer/Wave/ClientTransformer.php @@ -42,7 +42,7 @@ class ClientTransformer extends BaseTransformer 'company_id' => $this->company->id, 'name' => $this->getString($data, 'customer_name'), 'number' => $this->getValueOrNull($data, 'account_number'), - 'work_phone' => $this->getString($data, 'phone'), + 'phone' => $this->getString($data, 'phone'), 'website' => $this->getString($data, 'website'), 'country_id' => ! empty($data['country']) ? $this->getCountryId($data['country']) : null, 'state' => $this->getString($data, 'province/state'), diff --git a/app/Import/Transformers/ClientTransformer.php b/app/Import/Transformers/ClientTransformer.php index 1e7ed43ae226..a77af36b5693 100644 --- a/app/Import/Transformers/ClientTransformer.php +++ b/app/Import/Transformers/ClientTransformer.php @@ -35,7 +35,7 @@ class ClientTransformer extends BaseTransformer return [ 'company_id' => $this->maps['company']->id, 'name' => $this->getString($data, 'client.name'), - 'work_phone' => $this->getString($data, 'client.phone'), + 'phone' => $this->getString($data, 'client.phone'), 'address1' => $this->getString($data, 'client.address1'), 'address2' => $this->getString($data, 'client.address2'), 'city' => $this->getString($data, 'client.city'), diff --git a/app/Import/Transformers/Csv/ClientTransformer.php b/app/Import/Transformers/Csv/ClientTransformer.php index 55d0baa8a1fa..44f826e1bbb5 100644 --- a/app/Import/Transformers/Csv/ClientTransformer.php +++ b/app/Import/Transformers/Csv/ClientTransformer.php @@ -37,7 +37,7 @@ class ClientTransformer extends BaseTransformer return [ 'company_id' => $this->maps['company']->id, 'name' => $this->getString($data, 'client.name'), - 'work_phone' => $this->getString($data, 'client.phone'), + 'phone' => $this->getString($data, 'client.phone'), 'address1' => $this->getString($data, 'client.address1'), 'address2' => $this->getString($data, 'client.address2'), 'postal_code' => $this->getString($data, 'client.postal_code'), diff --git a/app/Import/Transformers/Freshbooks/ClientTransformer.php b/app/Import/Transformers/Freshbooks/ClientTransformer.php index bfa8743ed62d..5b82f400c223 100644 --- a/app/Import/Transformers/Freshbooks/ClientTransformer.php +++ b/app/Import/Transformers/Freshbooks/ClientTransformer.php @@ -34,7 +34,7 @@ class ClientTransformer extends BaseTransformer return [ 'company_id' => $this->maps['company']->id, 'name' => $this->getString($data, 'Organization'), - 'work_phone' => $this->getString($data, 'Phone'), + 'phone' => $this->getString($data, 'Phone'), 'address1' => $this->getString($data, 'Street'), 'city' => $this->getString($data, 'City'), 'state' => $this->getString($data, 'Province/State'), diff --git a/app/Import/Transformers/Invoicely/ClientTransformer.php b/app/Import/Transformers/Invoicely/ClientTransformer.php index ddf3645b3610..5e2d58ae4a09 100644 --- a/app/Import/Transformers/Invoicely/ClientTransformer.php +++ b/app/Import/Transformers/Invoicely/ClientTransformer.php @@ -34,7 +34,7 @@ class ClientTransformer extends BaseTransformer return [ 'company_id' => $this->maps['company']->id, 'name' => $this->getString($data, 'Client Name'), - 'work_phone' => $this->getString($data, 'Phone'), + 'phone' => $this->getString($data, 'Phone'), 'country_id' => isset($data['Country']) ? $this->getCountryIdBy2($data['Country']) : null, 'credit_balance' => 0, 'settings' => new \stdClass, diff --git a/app/Import/Transformers/Waveaccounting/ClientTransformer.php b/app/Import/Transformers/Waveaccounting/ClientTransformer.php index a19616f968f1..f9b95a42e487 100644 --- a/app/Import/Transformers/Waveaccounting/ClientTransformer.php +++ b/app/Import/Transformers/Waveaccounting/ClientTransformer.php @@ -42,7 +42,7 @@ class ClientTransformer extends BaseTransformer 'company_id' => $this->maps['company']->id, 'name' => $this->getString($data, 'customer_name'), 'number' => $this->getString($data, 'account_number'), - 'work_phone' => $this->getString($data, 'phone'), + 'phone' => $this->getString($data, 'phone'), 'website' => $this->getString($data, 'website'), 'country_id' => ! empty($data['country']) ? $this->getCountryId($data['country']) : null, 'state' => $this->getString($data, 'province/state'), diff --git a/app/Import/Transformers/Zoho/ClientTransformer.php b/app/Import/Transformers/Zoho/ClientTransformer.php index e8e618d189be..6895fabdaeb2 100644 --- a/app/Import/Transformers/Zoho/ClientTransformer.php +++ b/app/Import/Transformers/Zoho/ClientTransformer.php @@ -41,7 +41,7 @@ class ClientTransformer extends BaseTransformer return [ 'company_id' => $this->maps['company']->id, 'name' => $this->getString($data, 'Company Name'), - 'work_phone' => $this->getString($data, 'Phone'), + 'phone' => $this->getString($data, 'Phone'), 'private_notes' => $this->getString($data, 'Notes'), 'website' => $this->getString($data, 'Website'), 'id_number' => $this->getString($data, 'Customer ID'), diff --git a/app/Jobs/Bank/MatchBankTransactions.php b/app/Jobs/Bank/MatchBankTransactions.php index 9f5260e204ab..0532d51e8b58 100644 --- a/app/Jobs/Bank/MatchBankTransactions.php +++ b/app/Jobs/Bank/MatchBankTransactions.php @@ -266,7 +266,7 @@ class MatchBankTransactions implements ShouldQueue /* Create Payment */ $payment = PaymentFactory::create($this->invoice->company_id, $this->invoice->user_id); - $payment->amount = $amount; + $payment->amount = $this->bt->amount; $payment->applied = $this->applied_amount; $payment->status_id = Payment::STATUS_COMPLETED; $payment->client_id = $this->invoice->client_id; @@ -315,7 +315,7 @@ class MatchBankTransactions implements ShouldQueue $this->invoice ->client ->service() - ->updateBalanceAndPaidToDate($amount*-1, $amount) + ->updateBalanceAndPaidToDate($this->applied_amount*-1, $amount) ->save(); $this->invoice = $this->invoice diff --git a/app/Mail/Engine/CreditEmailEngine.php b/app/Mail/Engine/CreditEmailEngine.php index 12a12e600d88..25ac405d38ea 100644 --- a/app/Mail/Engine/CreditEmailEngine.php +++ b/app/Mail/Engine/CreditEmailEngine.php @@ -11,6 +11,7 @@ namespace App\Mail\Engine; +use App\Jobs\Entity\CreateRawPdf; use App\Models\Account; use App\Utils\HtmlEngine; use App\Utils\Ninja; @@ -117,11 +118,17 @@ class CreditEmailEngine extends BaseEmailEngine ->setTextBody($text_body); if ($this->client->getSetting('pdf_email_attachment') !== false && $this->credit->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { - if (Ninja::isHosted()) { - $this->setAttachments([$this->credit->pdf_file_path($this->invitation, 'url', true)]); - } else { - $this->setAttachments([$this->credit->pdf_file_path($this->invitation)]); - } + // if (Ninja::isHosted()) { + // $this->setAttachments([$this->credit->pdf_file_path($this->invitation, 'url', true)]); + // } else { + // $this->setAttachments([$this->credit->pdf_file_path($this->invitation)]); + // } + + $pdf = ((new CreateRawPdf($this->invitation, $this->invitation->company->db))->handle()); + + $this->setAttachments([['file' => base64_encode($pdf), 'name' => $this->credit->numberFormatter().'.pdf']]); + + } //attach third party documents @@ -129,11 +136,11 @@ class CreditEmailEngine extends BaseEmailEngine // Storage::url foreach ($this->credit->documents as $document) { - $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]); + $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, 'file' => base64_encode($document->getFile())]]); } foreach ($this->credit->company->documents as $document) { - $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]); + $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, 'file' => base64_encode($document->getFile())]]); } } diff --git a/app/Mail/Engine/InvoiceEmailEngine.php b/app/Mail/Engine/InvoiceEmailEngine.php index 000d2f72748f..69810ba626ca 100644 --- a/app/Mail/Engine/InvoiceEmailEngine.php +++ b/app/Mail/Engine/InvoiceEmailEngine.php @@ -13,6 +13,7 @@ namespace App\Mail\Engine; use App\DataMapper\EmailTemplateDefaults; use App\Jobs\Entity\CreateEntityPdf; +use App\Jobs\Entity\CreateRawPdf; use App\Models\Account; use App\Models\Expense; use App\Models\Task; @@ -126,11 +127,15 @@ class InvoiceEmailEngine extends BaseEmailEngine ->setTextBody($text_body); if ($this->client->getSetting('pdf_email_attachment') !== false && $this->invoice->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { - if (Ninja::isHosted()) { - $this->setAttachments([$this->invoice->pdf_file_path($this->invitation, 'url', true)]); - } else { - $this->setAttachments([$this->invoice->pdf_file_path($this->invitation)]); - } + // if (Ninja::isHosted()) { + // $this->setAttachments([$this->invoice->pdf_file_path($this->invitation, 'url', true)]); + // } else { + // $this->setAttachments([$this->invoice->pdf_file_path($this->invitation)]); + // } + // $file = (new CreateRawPdf($invitation, $invitation->company->db))->handle(); + $pdf = ((new CreateRawPdf($this->invitation, $this->invitation->company->db))->handle()); + + $this->setAttachments([['file' => base64_encode($pdf), 'name' => $this->invoice->numberFormatter().'.pdf']]); } //attach third party documents @@ -138,11 +143,11 @@ class InvoiceEmailEngine extends BaseEmailEngine // Storage::url foreach ($this->invoice->documents as $document) { - $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL]]); + $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]); } foreach ($this->invoice->company->documents as $document) { - $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL]]); + $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]); } $line_items = $this->invoice->line_items; @@ -160,7 +165,7 @@ class InvoiceEmailEngine extends BaseEmailEngine ->cursor() ->each(function ($expense) { foreach ($expense->documents as $document) { - $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL]]); + $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]); } }); } @@ -176,7 +181,7 @@ class InvoiceEmailEngine extends BaseEmailEngine ->cursor() ->each(function ($task) { foreach ($task->documents as $document) { - $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL]]); + $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]); } }); } diff --git a/app/Mail/Engine/PaymentEmailEngine.php b/app/Mail/Engine/PaymentEmailEngine.php index bf15b7aa2ca4..e9ad8767d284 100644 --- a/app/Mail/Engine/PaymentEmailEngine.php +++ b/app/Mail/Engine/PaymentEmailEngine.php @@ -12,6 +12,7 @@ namespace App\Mail\Engine; use App\DataMapper\EmailTemplateDefaults; +use App\Jobs\Entity\CreateRawPdf; use App\Models\Account; use App\Utils\Helpers; use App\Utils\Ninja; @@ -89,11 +90,15 @@ class PaymentEmailEngine extends BaseEmailEngine if ($this->client->getSetting('pdf_email_attachment') !== false && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { $this->payment->invoices->each(function ($invoice) { - if (Ninja::isHosted()) { - $this->setAttachments([$invoice->pdf_file_path($invoice->invitations->first(), 'url', true)]); - } else { - $this->setAttachments([$invoice->pdf_file_path($invoice->invitations->first())]); - } + // if (Ninja::isHosted()) { + // $this->setAttachments([$invoice->pdf_file_path($invoice->invitations->first(), 'url', true)]); + // } else { + // $this->setAttachments([$invoice->pdf_file_path($invoice->invitations->first())]); + // } + $pdf = ((new CreateRawPdf($invoice->invitations->first(), $invoice->company->db))->handle()); + + $this->setAttachments([['file' => base64_encode($pdf), 'name' => $invoice->numberFormatter().'.pdf']]); + }); } diff --git a/app/Mail/Engine/PurchaseOrderEmailEngine.php b/app/Mail/Engine/PurchaseOrderEmailEngine.php index fa6477c38c34..f11e046b68e2 100644 --- a/app/Mail/Engine/PurchaseOrderEmailEngine.php +++ b/app/Mail/Engine/PurchaseOrderEmailEngine.php @@ -13,6 +13,7 @@ namespace App\Mail\Engine; use App\DataMapper\EmailTemplateDefaults; use App\Jobs\Entity\CreateEntityPdf; +use App\Jobs\Vendor\CreatePurchaseOrderPdf; use App\Models\Account; use App\Models\Expense; use App\Models\PurchaseOrder; @@ -125,11 +126,16 @@ class PurchaseOrderEmailEngine extends BaseEmailEngine ->setTextBody($text_body); if ($this->vendor->getSetting('pdf_email_attachment') !== false && $this->purchase_order->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { - if (Ninja::isHosted()) { - $this->setAttachments([$this->purchase_order->pdf_file_path($this->invitation, 'url', true)]); - } else { - $this->setAttachments([$this->purchase_order->pdf_file_path($this->invitation)]); - } + // if (Ninja::isHosted()) { + // $this->setAttachments([$this->purchase_order->pdf_file_path($this->invitation, 'url', true)]); + // } else { + // $this->setAttachments([$this->purchase_order->pdf_file_path($this->invitation)]); + // } + + $pdf = (new CreatePurchaseOrderPdf($this->invitation))->rawPdf(); + + $this->setAttachments([['file' => base64_encode($pdf), 'name' => $this->purchase_order->numberFormatter().'.pdf']]); + } //attach third party documents @@ -138,10 +144,12 @@ class PurchaseOrderEmailEngine extends BaseEmailEngine // Storage::url foreach ($this->purchase_order->documents as $document) { $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]); + // $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, 'file' => base64_encode($document->getFile())]]); } foreach ($this->purchase_order->company->documents as $document) { $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]); + // $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, 'file' => base64_encode($document->getFile())]]); } } diff --git a/app/Mail/Engine/QuoteEmailEngine.php b/app/Mail/Engine/QuoteEmailEngine.php index 8b692c2f3e5d..63271c0600ba 100644 --- a/app/Mail/Engine/QuoteEmailEngine.php +++ b/app/Mail/Engine/QuoteEmailEngine.php @@ -11,6 +11,7 @@ namespace App\Mail\Engine; +use App\Jobs\Entity\CreateRawPdf; use App\Models\Account; use App\Utils\HtmlEngine; use App\Utils\Ninja; @@ -116,11 +117,15 @@ class QuoteEmailEngine extends BaseEmailEngine ->setTextBody($text_body); if ($this->client->getSetting('pdf_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { - if (Ninja::isHosted()) { - $this->setAttachments([$this->quote->pdf_file_path($this->invitation, 'url', true)]); - } else { - $this->setAttachments([$this->quote->pdf_file_path($this->invitation)]); - } + // if (Ninja::isHosted()) { + // $this->setAttachments([$this->quote->pdf_file_path($this->invitation, 'url', true)]); + // } else { + // $this->setAttachments([$this->quote->pdf_file_path($this->invitation)]); + // } + + $pdf = ((new CreateRawPdf($this->invitation, $this->invitation->company->db))->handle()); + + $this->setAttachments([['file' => base64_encode($pdf), 'name' => $this->quote->numberFormatter().'.pdf']]); } //attach third party documents @@ -128,11 +133,11 @@ class QuoteEmailEngine extends BaseEmailEngine // Storage::url foreach ($this->quote->documents as $document) { - $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]); + $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]); } foreach ($this->quote->company->documents as $document) { - $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => null]]); + $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, ]]); } } diff --git a/app/Mail/TemplateEmail.php b/app/Mail/TemplateEmail.php index cbf07442c075..b5b443657f78 100644 --- a/app/Mail/TemplateEmail.php +++ b/app/Mail/TemplateEmail.php @@ -120,45 +120,53 @@ class TemplateEmail extends Mailable /*In the hosted platform we need to slow things down a little for Storage to catch up.*/ - if(Ninja::isHosted() && $this->invitation){ + // if(Ninja::isHosted() && $this->invitation){ - $path = false; + // $path = false; - if($this->invitation->invoice) - $path = $this->client->invoice_filepath($this->invitation).$this->invitation->invoice->numberFormatter().'.pdf'; - elseif($this->invitation->quote) - $path = $this->client->quote_filepath($this->invitation).$this->invitation->quote->numberFormatter().'.pdf'; - elseif($this->invitation->credit) - $path = $this->client->credit_filepath($this->invitation).$this->invitation->credit->numberFormatter().'.pdf'; + // if($this->invitation->invoice) + // $path = $this->client->invoice_filepath($this->invitation).$this->invitation->invoice->numberFormatter().'.pdf'; + // elseif($this->invitation->quote) + // $path = $this->client->quote_filepath($this->invitation).$this->invitation->quote->numberFormatter().'.pdf'; + // elseif($this->invitation->credit) + // $path = $this->client->credit_filepath($this->invitation).$this->invitation->credit->numberFormatter().'.pdf'; - sleep(1); + // sleep(1); - if($path && !Storage::disk(config('filesystems.default'))->exists($path)){ + // if($path && !Storage::disk(config('filesystems.default'))->exists($path)){ - sleep(2); + // sleep(2); - if(!Storage::disk(config('filesystems.default'))->exists($path)) { - (new CreateEntityPdf($this->invitation))->handle(); - sleep(2); - } + // if(!Storage::disk(config('filesystems.default'))->exists($path)) { + // (new CreateEntityPdf($this->invitation))->handle(); + // sleep(2); + // } - } + // } + + // } + + // $file = (new CreateRawPdf($invitation, $invitation->company->db))->handle(); - } //22-10-2022 - Performance - To improve the performance/reliability of sending emails, attaching as Data is much better, stubs in place foreach ($this->build_email->getAttachments() as $file) { - if (is_string($file)) { - // nlog($file); - // $file_data = file_get_contents($file); - // $this->attachData($file_data, basename($file)); - $this->attach($file); - } elseif (is_array($file)) { - // nlog($file['path']); - // $file_data = file_get_contents($file['path']); - // $this->attachData($file_data, $file['name']); + // if (is_string($file)) { + // // nlog($file); + // // $file_data = file_get_contents($file); + // // $this->attachData($file_data, basename($file)); + // $this->attach($file); + // } elseif (is_array($file)) { + // // nlog($file['path']); + // // $file_data = file_get_contents($file['path']); + // // $this->attachData($file_data, $file['name']); + // $this->attach($file['path'], ['as' => $file['name'], 'mime' => null]); + // } + if(array_key_exists('file', $file)) + $this->attachData(base64_decode($file['file']), $file['name']); + else $this->attach($file['path'], ['as' => $file['name'], 'mime' => null]); - } + } if ($this->invitation && $this->invitation->invoice && $settings->ubl_email_attachment && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { diff --git a/app/Mail/VendorTemplateEmail.php b/app/Mail/VendorTemplateEmail.php index 670a59e93676..ccf9ef06955e 100644 --- a/app/Mail/VendorTemplateEmail.php +++ b/app/Mail/VendorTemplateEmail.php @@ -110,40 +110,39 @@ class VendorTemplateEmail extends Mailable 'whitelabel' => $this->vendor->user->account->isPaid() ? true : false, 'logo' => $this->company->present()->logo($settings), ]); - //->withSymfonyMessage(function ($message) { - // $message->getHeaders()->addTextHeader('Tag', $this->company->company_key); - // $message->invitation = $this->invitation; - //}); - // ->tag($this->company->company_key); - if(Ninja::isHosted() && $this->invitation){ - $path = false; + // if(Ninja::isHosted() && $this->invitation){ - if($this->invitation->purchase_order) - $path = $this->vendor->purchase_order_filepath($this->invitation).$this->invitation->purchase_order->numberFormatter().'.pdf'; + // $path = false; - sleep(1); + // if($this->invitation->purchase_order) + // $path = $this->vendor->purchase_order_filepath($this->invitation).$this->invitation->purchase_order->numberFormatter().'.pdf'; - if($path && !Storage::disk(config('filesystems.default'))->exists($path)){ + // sleep(1); - sleep(2); + // if($path && !Storage::disk(config('filesystems.default'))->exists($path)){ - if(!Storage::disk(config('filesystems.default'))->exists($path)) { - (new CreatePurchaseOrderPdf($this->invitation))->handle(); - sleep(2); - } + // sleep(2); - } + // if(!Storage::disk(config('filesystems.default'))->exists($path)) { + // (new CreatePurchaseOrderPdf($this->invitation))->handle(); + // sleep(2); + // } - } + // } + + // } foreach ($this->build_email->getAttachments() as $file) { - if (is_string($file)) { - $this->attach($file); - } elseif (is_array($file)) { - $this->attach($file['path'], ['as' => $file['name'], 'mime' => null]); - } + // if (is_string($file)) { + // $this->attach($file); + // } elseif (is_array($file)) { + // $this->attach($file['path'], ['as' => $file['name'], 'mime' => null]); + // } + + $this->attachData(base64_decode($file['file']), $file['name']); + } return $this; diff --git a/app/Models/Presenters/CompanyPresenter.php b/app/Models/Presenters/CompanyPresenter.php index 57408a788590..9d44bee8c60d 100644 --- a/app/Models/Presenters/CompanyPresenter.php +++ b/app/Models/Presenters/CompanyPresenter.php @@ -119,7 +119,7 @@ class CompanyPresenter extends EntityPresenter $str .= e($country->name).'
'; } if ($settings->phone) { - $str .= ctrans('texts.work_phone').': '.e($settings->phone).'
'; + $str .= ctrans('texts.phone').': '.e($settings->phone).'
'; } if ($settings->email) { $str .= ctrans('texts.work_email').': '.e($settings->email).'
'; diff --git a/app/PaymentDrivers/CheckoutCom/CreditCard.php b/app/PaymentDrivers/CheckoutCom/CreditCard.php index 27e6c4d67243..1ada04574b5e 100644 --- a/app/PaymentDrivers/CheckoutCom/CreditCard.php +++ b/app/PaymentDrivers/CheckoutCom/CreditCard.php @@ -94,8 +94,6 @@ class CreditCard implements MethodInterface $customerRequest = $this->checkout->getCustomer(); - nlog($customerRequest); - $request = $this->bootRequest($gateway_response->token); $request->capture = false; $request->reference = '$1 payment for authorization.'; diff --git a/app/PaymentDrivers/CheckoutComPaymentDriver.php b/app/PaymentDrivers/CheckoutComPaymentDriver.php index daa9aa7fcd68..c59534a05788 100644 --- a/app/PaymentDrivers/CheckoutComPaymentDriver.php +++ b/app/PaymentDrivers/CheckoutComPaymentDriver.php @@ -34,6 +34,7 @@ use Checkout\CheckoutArgumentException; use Checkout\CheckoutAuthorizationException; use Checkout\CheckoutDefaultSdk; use Checkout\CheckoutFourSdk; +use Checkout\Common\Phone; use Checkout\Customers\CustomerRequest; use Checkout\Customers\Four\CustomerRequest as FourCustomerRequest; use Checkout\Environment; @@ -300,9 +301,12 @@ class CheckoutComPaymentDriver extends BaseDriver $request = new CustomerRequest(); } - $request->email = $this->client->present()->email(); - $request->name = $this->client->present()->name(); - $request->phone = $this->client->present()->phone(); + $phone = new Phone(); + $phone->number = $this->client->present()->phone(); + + $request->email = $this->client->present()->email(); + $request->name = $this->client->present()->name(); + $request->phone = $phone; try { $response = $this->gateway->getCustomersClient()->create($request); diff --git a/app/PaymentDrivers/Stripe/ACH.php b/app/PaymentDrivers/Stripe/ACH.php index cc25056ca673..8e8a8f70ebf4 100644 --- a/app/PaymentDrivers/Stripe/ACH.php +++ b/app/PaymentDrivers/Stripe/ACH.php @@ -36,6 +36,7 @@ use Stripe\Exception\CardException; use Stripe\Exception\InvalidRequestException; use Stripe\Exception\RateLimitException; use Stripe\PaymentIntent; +use App\Utils\Number; class ACH { @@ -172,9 +173,9 @@ class ACH ->first(); if ($invoice) { - $description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}"; + $description = ctrans('texts.stripe_paymenttext', ['invoicenumber' => $invoice->number, 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]); } else { - $description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}"; + $description = ctrans('text.stripe_paymenttext_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]); } @@ -210,9 +211,9 @@ class ACH ->first(); if ($invoice) { - $description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}"; + $description = ctrans('texts.stripe_paymenttext', ['invoicenumber' => $invoice->number, 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]); } else { - $description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}"; + $description = ctrans('text.stripe_paymenttext_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]); } if (substr($cgt->token, 0, 2) === 'pm') { @@ -454,9 +455,9 @@ class ACH ->first(); if ($invoice) { - $description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}"; + $description = ctrans('texts.stripe_paymenttext', ['invoicenumber' => $invoice->number, 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]); } else { - $description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}"; + $description = ctrans('text.stripe_paymenttext_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]); } if (substr($source->token, 0, 2) === 'pm') { diff --git a/app/PaymentDrivers/Stripe/Charge.php b/app/PaymentDrivers/Stripe/Charge.php index 7dbd60c07f57..dabef4a6fbf6 100644 --- a/app/PaymentDrivers/Stripe/Charge.php +++ b/app/PaymentDrivers/Stripe/Charge.php @@ -32,6 +32,7 @@ use Stripe\Exception\CardException; use Stripe\Exception\InvalidRequestException; use Stripe\Exception\RateLimitException; use Stripe\StripeClient; +use App\Utils\Number; class Charge { @@ -62,9 +63,9 @@ class Charge $invoice = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->withTrashed()->first(); if ($invoice) { - $description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}"; + $description = ctrans('texts.stripe_paymenttext', ['invoicenumber' => $invoice->number, 'amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]); } else { - $description = "Payment with no invoice for amount {$amount} for client {$this->stripe->client->present()->name()}"; + $description = ctrans('text.stripe_paymenttext_without_invoice', ['amount' => Number::formatMoney($amount, $this->stripe->client), 'client' => $this->stripe->client->present()->name()]); } $this->stripe->init(); diff --git a/app/PaymentDrivers/Stripe/CreditCard.php b/app/PaymentDrivers/Stripe/CreditCard.php index 838af9c2328c..64b9df528317 100644 --- a/app/PaymentDrivers/Stripe/CreditCard.php +++ b/app/PaymentDrivers/Stripe/CreditCard.php @@ -23,6 +23,7 @@ use App\PaymentDrivers\StripePaymentDriver; use App\PaymentDrivers\Stripe\Jobs\UpdateCustomer; use Stripe\PaymentIntent; use Stripe\PaymentMethod; +use App\Utils\Number; class CreditCard { @@ -62,7 +63,7 @@ class CreditCard // $description = $this->stripe->decodeUnicodeString(ctrans('texts.invoices') . ': ' . collect($data['invoices'])->pluck('invoice_number')) . " for client {$this->stripe->client->present()->name()}"; $invoice_numbers = collect($data['invoices'])->pluck('invoice_number')->implode(','); - $description = "Invoices: {$invoice_numbers} for {$data['total']['amount_with_fee']} for client {$this->stripe->client->present()->name()}"; + $description = ctrans('texts.stripe_paymenttext', ['invoicenumber' => $invoice_numbers, 'amount' => Number::formatMoney($data['total']['amount_with_fee'], $this->stripe->client), 'client' => $this->stripe->client->present()->name()]); $payment_intent_data = [ 'amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()), diff --git a/app/PaymentDrivers/Stripe/Jobs/PaymentIntentWebhook.php b/app/PaymentDrivers/Stripe/Jobs/PaymentIntentWebhook.php index 1caf382e567f..d3a8ad40e7d6 100644 --- a/app/PaymentDrivers/Stripe/Jobs/PaymentIntentWebhook.php +++ b/app/PaymentDrivers/Stripe/Jobs/PaymentIntentWebhook.php @@ -66,11 +66,9 @@ class PaymentIntentWebhook implements ShouldQueue { $payment = Payment::query() ->where('company_id', $company->id) - ->where(function ($query) use ($transaction) { - $query->where('transaction_reference', $transaction['payment_intent']) - ->orWhere('transaction_reference', $transaction['id']); - }) + ->where('transaction_reference', $transaction['payment_intent']) ->first(); + } else { diff --git a/app/Repositories/ClientRepository.php b/app/Repositories/ClientRepository.php index 23c6ae8cee28..6114916e80ad 100644 --- a/app/Repositories/ClientRepository.php +++ b/app/Repositories/ClientRepository.php @@ -56,6 +56,8 @@ class ClientRepository extends BaseRepository */ public function save(array $data, Client $client) : ?Client { + $contact_data = $data; + unset($data['contacts']); /* When uploading documents, only the document array is sent, so we must return early*/ if (array_key_exists('documents', $data) && count($data['documents']) >= 1) { @@ -67,7 +69,7 @@ class ClientRepository extends BaseRepository $client->fill($data); if (array_key_exists('settings', $data)) { - $client->saveSettings($data['settings'], $client); + $client->settings = $client->saveSettings($data['settings'], $client); } if (! $client->country_id) { @@ -75,19 +77,9 @@ class ClientRepository extends BaseRepository $client->country_id = $company->settings->country_id; } - try{ - $client->save(); - } - catch(\Exception $e) { - - nlog("client save failed"); - nlog($data); - - } + $client->save(); if (! isset($client->number) || empty($client->number) || strlen($client->number) == 0) { - // $client->number = $this->getNextClientNumber($client); - // $client->save(); $x = 1; @@ -111,7 +103,7 @@ class ClientRepository extends BaseRepository $data['name'] = $client->present()->name(); } - $this->contact_repo->save($data, $client); + $this->contact_repo->save($contact_data, $client); return $client; } diff --git a/app/Services/Invoice/HandleRestore.php b/app/Services/Invoice/HandleRestore.php index 412013aceb4c..0d46f8717e40 100644 --- a/app/Services/Invoice/HandleRestore.php +++ b/app/Services/Invoice/HandleRestore.php @@ -44,11 +44,12 @@ class HandleRestore extends AbstractService return $this->invoice; } - //determine whether we need to un-delete payments OR just modify the payment amount /applied balances. - + //cannot restore an invoice with a deleted payment foreach ($this->invoice->payments as $payment) { - //restore the payment record - $this->invoice->restore(); + + if(($this->invoice->paid_to_date == 0) && $payment->is_deleted) + return $this->invoice; + } //adjust ledger balance @@ -56,8 +57,7 @@ class HandleRestore extends AbstractService $this->invoice->client ->service() - ->updateBalance($this->invoice->balance) - ->updatePaidToDate($this->invoice->paid_to_date) + ->updateBalanceAndPaidToDate($this->invoice->balance,$this->invoice->paid_to_date) ->save(); $this->windBackInvoiceNumber(); @@ -120,11 +120,11 @@ class HandleRestore extends AbstractService if ($this->adjustment_amount == $this->total_payments) { $this->invoice->payments()->update(['payments.deleted_at' => null, 'payments.is_deleted' => false]); - } else { + } //adjust payments down by the amount applied to the invoice payment. - $this->invoice->payments->each(function ($payment) { + $this->invoice->payments->fresh()->each(function ($payment) { $payment_adjustment = $payment->paymentables ->where('paymentable_type', '=', 'invoices') ->where('paymentable_id', $this->invoice->id) @@ -141,8 +141,7 @@ class HandleRestore extends AbstractService $payment->restore(); $payment->save(); }); - } - + return $this; } diff --git a/app/Services/Invoice/MarkInvoiceDeleted.php b/app/Services/Invoice/MarkInvoiceDeleted.php index b16c468c7c1e..e317c403ade6 100644 --- a/app/Services/Invoice/MarkInvoiceDeleted.php +++ b/app/Services/Invoice/MarkInvoiceDeleted.php @@ -53,16 +53,6 @@ class MarkInvoiceDeleted extends AbstractService ->adjustPaidToDateAndBalance() ->adjustLedger(); - $transaction = [ - 'invoice' => $this->invoice->transaction_event(), - 'payment' => $this->invoice->payments()->exists() ? $this->invoice->payments()->first()->transaction_event() : [], - 'client' => $this->invoice->client->transaction_event(), - 'credit' => [], - 'metadata' => ['total_payments' => $this->total_payments, 'balance_adjustment' => $this->balance_adjustment, 'adjustment_amount' => $this->adjustment_amount], - ]; - - // TransactionLog::dispatch(TransactionEvent::INVOICE_DELETED, $transaction, $this->invoice->company->db); - return $this->invoice; } @@ -87,26 +77,17 @@ class MarkInvoiceDeleted extends AbstractService return $this; } - // @deprecated - private function adjustBalance() - { - // $client = $this->invoice->client->fresh(); - // $client->balance += $this->balance_adjustment * -1; - // $client->save(); - - // $this->invoice->client->service()->updateBalance($this->balance_adjustment * -1)->save(); //reduces the client balance by the invoice amount. - - return $this; - } - /* Adjust the payment amounts */ private function adjustPayments() { //if total payments = adjustment amount - that means we need to delete the payments as well. - if ($this->adjustment_amount == $this->total_payments) { +nlog($this->adjustment_amount); +nlog($this->total_payments); + + if ($this->adjustment_amount == $this->total_payments) $this->invoice->payments()->update(['payments.deleted_at' => now(), 'payments.is_deleted' => true]); - } else { + //adjust payments down by the amount applied to the invoice payment. @@ -125,7 +106,7 @@ class MarkInvoiceDeleted extends AbstractService $payment->applied -= $payment_adjustment; $payment->save(); }); - } + return $this; } diff --git a/app/Utils/HostedPDF/NinjaPdf.php b/app/Utils/HostedPDF/NinjaPdf.php index 7a6da2097b53..e0598b54c928 100644 --- a/app/Utils/HostedPDF/NinjaPdf.php +++ b/app/Utils/HostedPDF/NinjaPdf.php @@ -28,6 +28,6 @@ class NinjaPdf RequestOptions::JSON => ['html' => $html], ]); - return $response->getBody(); + return $response->getBody()->getContents(); } } diff --git a/app/Utils/Traits/ClientGroupSettingsSaver.php b/app/Utils/Traits/ClientGroupSettingsSaver.php index 67c2b6a774b3..270f2e0588cb 100644 --- a/app/Utils/Traits/ClientGroupSettingsSaver.php +++ b/app/Utils/Traits/ClientGroupSettingsSaver.php @@ -30,7 +30,7 @@ trait ClientGroupSettingsSaver * Saves a setting object. * * Works for groups|clients|companies - * @param array $settings The request input settings array + * @param array|object $settings The request input settings array * @param object $entity The entity which the settings belongs to * @return void */ @@ -64,19 +64,6 @@ trait ClientGroupSettingsSaver $entity_settings->{$key} = $value; } - $entity->settings = $entity_settings; - - try{ - $entity->save(); - } - catch(\Exception $e){ - - nlog("client settings failure"); - nlog($entity_settings); - nlog($e->getMessage()); - - } - return $entity_settings; } diff --git a/config/ninja.php b/config/ninja.php index 314b877deb28..15fec9f598ce 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -14,8 +14,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => '5.5.43', - 'app_tag' => '5.5.43', + 'app_version' => '5.5.44', + 'app_tag' => '5.5.44', 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', ''), diff --git a/lang/en/texts.php b/lang/en/texts.php index 1bc294749e4a..a98bcf88145e 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -254,6 +254,8 @@ $LANG = array( 'notification_invoice_paid' => 'A payment of :amount was made by client :client towards Invoice :invoice.', 'notification_invoice_sent' => 'The following client :client was emailed Invoice :invoice for :amount.', 'notification_invoice_viewed' => 'The following client :client viewed Invoice :invoice for :amount.', + 'stripe_paymenttext' => 'Invoice :invoicenumber for :amount for client :client', + 'stripe_paymenttext_without_invoice' => 'Payment with no invoice for amount :amount for client :client', 'reset_password' => 'You can reset your account password by clicking the following button:', 'secure_payment' => 'Secure Payment', 'card_number' => 'Card Number', @@ -4061,7 +4063,7 @@ $LANG = array( 'save_payment_method_details' => 'Save payment method details', 'new_card' => 'New card', 'new_bank_account' => 'New bank account', - 'company_limit_reached' => 'Limit of 10 companies per account.', + 'company_limit_reached' => 'Limit of :limit companies per account.', 'credits_applied_validation' => 'Total credits applied cannot be MORE than total of invoices', 'credit_number_taken' => 'Credit number already taken', 'credit_not_found' => 'Credit not found', diff --git a/tests/Feature/ClientApiTest.php b/tests/Feature/ClientApiTest.php index aae531575656..40b11fc70a4a 100644 --- a/tests/Feature/ClientApiTest.php +++ b/tests/Feature/ClientApiTest.php @@ -11,12 +11,20 @@ namespace Tests\Feature; +use App\DataMapper\ClientSettings; +use App\Factory\ClientFactory; +use App\Http\Requests\Client\StoreClientRequest; +use App\Models\Client; use App\Models\Country; +use App\Repositories\ClientContactRepository; +use App\Repositories\ClientRepository; use App\Utils\Number; +use App\Utils\Traits\ClientGroupSettingsSaver; use App\Utils\Traits\MakesHash; use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\Session; +use Illuminate\Support\Facades\Validator; use Illuminate\Validation\ValidationException; use Tests\MockAccountData; use Tests\TestCase; @@ -30,6 +38,7 @@ class ClientApiTest extends TestCase use MakesHash; use DatabaseTransactions; use MockAccountData; + use ClientGroupSettingsSaver; protected function setUp() :void { @@ -44,6 +53,302 @@ class ClientApiTest extends TestCase Model::reguard(); } + public function testCsvImportRepositoryPersistance() + { + Client::unguard(); + + $data = [ + 'company_id' => $this->company->id, + 'name' => 'Christian xx', + 'phone' => '', + 'address1' => '', + 'address2' => '', + 'postal_code' => '', + 'city' => '', + 'state' => '', + 'shipping_address1' => '', + 'shipping_address2' => '', + 'shipping_city' => '', + 'shipping_state' => '', + 'shipping_postal_code' => '', + 'public_notes' => '', + 'private_notes' => '', + 'website' => '', + 'vat_number' => '', + 'id_number' => '', + 'custom_value1' => '', + 'custom_value2' => '', + 'custom_value3' => '', + 'custom_value4' => '', + 'balance' => '0', + 'paid_to_date' => '0', + 'credit_balance' => 0, + 'settings' => [ + 'entity' => 'App\\Models\\Client', + 'currency_id' => '3', + ], + 'client_hash' => 'xx', + 'contacts' => + [ + [ + 'first_name' => '', + 'last_name' => '', + 'email' => '', + 'phone' => '', + 'custom_value1' => '', + 'custom_value2' => '', + 'custom_value3' => '', + 'custom_value4' => '', + ] + ], + 'country_id' => NULL, + 'shipping_country_id' => NULL, + 'user_id' => $this->user->id, + ]; + + $repository_name = ClientRepository::class; + $factory_name = ClientFactory::class; + + $repository = app()->make($repository_name); + $repository->import_mode = true; + + $c = $repository->save(array_diff_key($data, ['user_id' => false]), ClientFactory::create($this->company->id, $this->user->id)); + + Client::reguard(); + + $c->refresh(); + + $this->assertEquals("3", $c->settings->currency_id); + + } + + public function testClientSettingsSave() + { + + $std = new \stdClass; + $std->entity = 'App\\Models\\Client'; + $std->currency_id = 3; + + $this->settings = $this->client->settings; + + $this->saveSettings($std, $this->client); + + $this->assertTrue(true); + + } + + + public function testClientSettingsSave2() + { + + $std = new \stdClass; + $std->entity = 'App\\Models\\Client'; + $std->industry_id = ''; + $std->size_id = ''; + $std->currency_id = 3; + + $this->settings = $this->client->settings; + + $this->saveSettings($std, $this->client); + + $this->assertTrue(true); + + } + + public function testClientStoreValidation() + { + + auth()->login($this->user, false); + auth()->user()->setCompany($this->company); + + $data = array ( + 'company_id' => $this->company->id, + 'name' => 'Christian xx', + 'phone' => '', + 'address1' => '', + 'address2' => '', + 'postal_code' => '', + 'city' => '', + 'state' => '', + 'shipping_address1' => '', + 'shipping_address2' => '', + 'shipping_city' => '', + 'shipping_state' => '', + 'shipping_postal_code' => '', + 'public_notes' => '', + 'private_notes' => '', + 'website' => '', + 'vat_number' => '', + 'id_number' => '', + 'custom_value1' => '', + 'custom_value2' => '', + 'custom_value3' => '', + 'custom_value4' => '', + 'balance' => '0', + 'paid_to_date' => '0', + 'credit_balance' => 0, + 'settings' => + (object) array( + 'entity' => 'App\\Models\\Client', + 'currency_id' => '3', + ), + 'client_hash' => 'xx', + 'contacts' => + array ( + 0 => + array ( + 'first_name' => '', + 'last_name' => '', + 'email' => '', + 'phone' => '', + 'custom_value1' => '', + 'custom_value2' => '', + 'custom_value3' => '', + 'custom_value4' => '', + ), + ), + 'country_id' => NULL, + 'shipping_country_id' => NULL, + 'user_id' => $this->user->id, + ); + + + $request_name = StoreClientRequest::class; + $repository_name = ClientRepository::class; + $factory_name = ClientFactory::class; + + $repository = app()->make($repository_name); + $repository->import_mode = true; + + $_syn_request_class = new $request_name; + $_syn_request_class->setContainer(app()); + $_syn_request_class->initialize($data); + $_syn_request_class->prepareForValidation(); + + $validator = Validator::make($_syn_request_class->all(), $_syn_request_class->rules()); + + $_syn_request_class->setValidator($validator); + + $this->assertFalse($validator->fails()); + + + } + + + + public function testClientImportDataStructure() + { + + + $data = array ( + 'company_id' => $this->company->id, + 'name' => 'Christian xx', + 'phone' => '', + 'address1' => '', + 'address2' => '', + 'postal_code' => '', + 'city' => '', + 'state' => '', + 'shipping_address1' => '', + 'shipping_address2' => '', + 'shipping_city' => '', + 'shipping_state' => '', + 'shipping_postal_code' => '', + 'public_notes' => '', + 'private_notes' => '', + 'website' => '', + 'vat_number' => '', + 'id_number' => '', + 'custom_value1' => '', + 'custom_value2' => '', + 'custom_value3' => '', + 'custom_value4' => '', + 'balance' => '0', + 'paid_to_date' => '0', + 'credit_balance' => 0, + 'settings' => + (object) array( + 'entity' => 'App\\Models\\Client', + 'currency_id' => '3', + ), + 'client_hash' => 'xx', + 'contacts' => + array ( + 0 => + array ( + 'first_name' => '', + 'last_name' => '', + 'email' => '', + 'phone' => '', + 'custom_value1' => '', + 'custom_value2' => '', + 'custom_value3' => '', + 'custom_value4' => '', + ), + ), + 'country_id' => NULL, + 'shipping_country_id' => NULL, + 'user_id' => $this->user->id, + ); + + $crepo = new ClientRepository(new ClientContactRepository()); + + $c = $crepo->save(array_diff_key($data, ['user_id' => false]), ClientFactory::create($this->company->id, $this->user->id)); + $c->saveQuietly(); + + $this->assertEquals('Christian xx', $c->name); + $this->assertEquals('3', $c->settings->currency_id); + } + + public function testClientCsvImport() + { + + $settings = ClientSettings::defaults(); + $settings->currency_id = "840"; + + $data = [ + 'name' => $this->faker->firstName(), + 'id_number' => 'Coolio', + 'settings' => (array)$settings, + 'contacts' => [ + [ + 'first_name' => '', + 'last_name' => '', + 'email' => '', + 'phone' => '', + 'custom_value1' => '', + 'custom_value2' => '', + 'custom_value3' => '', + 'custom_value4' => '', + ] + ] + ]; + + $response = false; + + try { + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/clients/', $data); + } catch (ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(), 1); + nlog($message); + } + + $response->assertStatus(200); + + $crepo = new ClientRepository(new ClientContactRepository()); + + $c = $crepo->save($data, ClientFactory::create($this->company->id, $this->user->id)); + $c->saveQuietly(); + + + } + + + + public function testIllegalPropertiesInClientSettings() { $settings = [ diff --git a/tests/Feature/DeleteInvoiceTest.php b/tests/Feature/DeleteInvoiceTest.php index d49f6f27c9e9..8dcf2daf4764 100644 --- a/tests/Feature/DeleteInvoiceTest.php +++ b/tests/Feature/DeleteInvoiceTest.php @@ -162,6 +162,7 @@ class DeleteInvoiceTest extends TestCase $payment = $payment->fresh(); $this->assertTrue($payment->is_deleted); + $this->assertEquals(0, $payment->amount); $this->assertEquals(4, $payment->status_id); $client->fresh();