Merge pull request #8012 from turbo124/v5-stable

v5.5.44
This commit is contained in:
David Bomba 2022-11-30 17:23:11 +11:00 committed by GitHub
commit 4d3200fad2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 532 additions and 201 deletions

View File

@ -1 +1 @@
5.5.43 5.5.44

View File

@ -54,6 +54,7 @@ class RecurringInvoiceExport extends BaseExport
'po_number' => 'po_number', 'po_number' => 'po_number',
'private_notes' => 'private_notes', 'private_notes' => 'private_notes',
'public_notes' => 'public_notes', 'public_notes' => 'public_notes',
'next_send_date' => 'next_send_date',
'status' => 'status_id', 'status' => 'status_id',
'tax_name1' => 'tax_name1', 'tax_name1' => 'tax_name1',
'tax_name2' => 'tax_name2', 'tax_name2' => 'tax_name2',
@ -66,6 +67,7 @@ class RecurringInvoiceExport extends BaseExport
'currency' => 'currency_id', 'currency' => 'currency_id',
'vendor' => 'vendor_id', 'vendor' => 'vendor_id',
'project' => 'project_id', 'project' => 'project_id',
'frequency' => 'frequency_id'
]; ];
private array $decorate_keys = [ private array $decorate_keys = [
@ -162,6 +164,8 @@ class RecurringInvoiceExport extends BaseExport
$entity['vendor'] = $invoice->vendor ? $invoice->vendor->name : ''; $entity['vendor'] = $invoice->vendor ? $invoice->vendor->name : '';
} }
$entity['frequency'] = $invoice->frequencyForKey($invoice->frequency_id);
return $entity; return $entity;
} }
} }

View File

@ -11,6 +11,7 @@
namespace App\Filters; namespace App\Filters;
use App\Models\Company;
use App\Models\User; use App\Models\User;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
@ -54,6 +55,15 @@ class DocumentFilters extends QueryFilters
return $this->builder->orderBy($sort_col[0], $sort_col[1]); 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. * Returns the base query.
* *

View File

@ -131,8 +131,6 @@ class EmailController extends BaseController
if(Ninja::isHosted() && !$entity_obj->company->account->account_sms_verified) if(Ninja::isHosted() && !$entity_obj->company->account->account_sms_verified)
return response(['message' => 'Please verify your account to send emails.'], 400); 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'){ if($entity == 'purchaseOrder' || $entity == 'purchase_order' || $template == 'purchase_order' || $entity == 'App\Models\PurchaseOrder'){
return $this->sendPurchaseOrder($entity_obj, $data, $template); return $this->sendPurchaseOrder($entity_obj, $data, $template);
} }
@ -141,7 +139,7 @@ class EmailController extends BaseController
if (! $invitation->contact->trashed() && $invitation->contact->email) { if (! $invitation->contact->trashed() && $invitation->contact->email) {
$entity_obj->service()->markSent()->save(); $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; $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); return $this->itemResponse($entity_obj);

View File

@ -136,6 +136,8 @@ class ImportController extends Controller
} }
$csv = Reader::createFromString($csvfile); $csv = Reader::createFromString($csvfile);
$csvdelimiter = self::detectDelimiter($csvfile);
$csv->setDelimiter($csvdelimiter);
$stmt = new Statement(); $stmt = new Statement();
$data = iterator_to_array($stmt->process($csv)); $data = iterator_to_array($stmt->process($csv));
@ -156,4 +158,17 @@ class ImportController extends Controller
return $data; 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;
}
} }

View File

@ -26,7 +26,7 @@
* @OA\Property(property="city", type="string", example="", description="________"), * @OA\Property(property="city", type="string", example="", description="________"),
* @OA\Property(property="state", type="string", example="", description="________"), * @OA\Property(property="state", type="string", example="", description="________"),
* @OA\Property(property="postal_code", 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="country_id", type="string", example="", description="________"),
* @OA\Property(property="currency_id", type="string", example="4", description="________"), * @OA\Property(property="currency_id", type="string", example="4", description="________"),
* @OA\Property(property="custom_value1", type="string", example="", description="________"), * @OA\Property(property="custom_value1", type="string", example="", description="________"),

View File

@ -44,7 +44,7 @@ class StoreBankIntegrationRequest extends Request
{ {
$input = $this->all(); $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']; $input['provider_name'] = $input['bank_account_name'];
$this->replace($input); $this->replace($input);

View File

@ -34,7 +34,6 @@ class StoreBankTransactionRequest extends Request
$rules = []; $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; return $rules;
@ -44,7 +43,9 @@ class StoreBankTransactionRequest extends Request
{ {
$input = $this->all(); $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']); $input['bank_integration_id'] = $this->decodePrimaryKey($input['bank_integration_id']);
$this->replace($input); $this->replace($input);

View File

@ -45,7 +45,6 @@ class UpdateBankTransactionRequest extends Request
if(isset($this->expense_id)) if(isset($this->expense_id))
$rules['expense_id'] = 'bail|required|exists:expenses,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; $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';
@ -69,7 +68,9 @@ class UpdateBankTransactionRequest extends Request
if(array_key_exists('ninja_category_id', $input) && strlen($input['ninja_category_id']) > 1) if(array_key_exists('ninja_category_id', $input) && strlen($input['ninja_category_id']) > 1)
$input['ninja_category_id'] = $this->decodePrimaryKey($input['ninja_category_id']); $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']); $input['bank_integration_id'] = $this->decodePrimaryKey($input['bank_integration_id']);
$this->replace($input); $this->replace($input);

View File

@ -45,6 +45,8 @@ class StoreExpenseRequest extends Request
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id; $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); return $this->globalRules($rules);
} }
@ -54,10 +56,6 @@ class StoreExpenseRequest extends Request
$input = $this->decodePrimaryKeys($input); $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) { if (! array_key_exists('currency_id', $input) || strlen($input['currency_id']) == 0) {
$input['currency_id'] = (string) auth()->user()->company()->settings->currency_id; $input['currency_id'] = (string) auth()->user()->company()->settings->currency_id;
} }
@ -66,7 +64,6 @@ class StoreExpenseRequest extends Request
$input['color'] = ''; $input['color'] = '';
} }
/* Ensure the project is related */ /* Ensure the project is related */
if (array_key_exists('project_id', $input) && isset($input['project_id'])) { if (array_key_exists('project_id', $input) && isset($input['project_id'])) {
$project = Project::withTrashed()->where('id', $input['project_id'])->company()->first(); $project = Project::withTrashed()->where('id', $input['project_id'])->company()->first();

View File

@ -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['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); return $this->globalRules($rules);
} }
@ -50,10 +52,6 @@ class UpdateExpenseRequest extends Request
$input = $this->decodePrimaryKeys($input); $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)) { if (array_key_exists('documents', $input)) {
unset($input['documents']); unset($input['documents']);
} }

View File

@ -39,6 +39,6 @@ class ValidCompanyQuantity implements Rule
*/ */
public function message() 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]);
} }
} }

View File

@ -38,7 +38,7 @@ class ClientTransformer extends BaseTransformer
return [ return [
'company_id' => $this->company->id, 'company_id' => $this->company->id,
'name' => $this->getString($data, 'client.name'), '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'), 'address1' => $this->getString($data, 'client.address1'),
'address2' => $this->getString($data, 'client.address2'), 'address2' => $this->getString($data, 'client.address2'),
'postal_code' => $this->getString($data, 'client.postal_code'), 'postal_code' => $this->getString($data, 'client.postal_code'),

View File

@ -42,7 +42,7 @@ class ClientTransformer extends BaseTransformer
'company_id' => $this->company->id, 'company_id' => $this->company->id,
'name' => $this->getString($data, 'customer_name'), 'name' => $this->getString($data, 'customer_name'),
'number' => $this->getValueOrNull($data, 'account_number'), 'number' => $this->getValueOrNull($data, 'account_number'),
'work_phone' => $this->getString($data, 'phone'), 'phone' => $this->getString($data, 'phone'),
'website' => $this->getString($data, 'website'), 'website' => $this->getString($data, 'website'),
'country_id' => ! empty($data['country']) ? $this->getCountryId($data['country']) : null, 'country_id' => ! empty($data['country']) ? $this->getCountryId($data['country']) : null,
'state' => $this->getString($data, 'province/state'), 'state' => $this->getString($data, 'province/state'),

View File

@ -35,7 +35,7 @@ class ClientTransformer extends BaseTransformer
return [ return [
'company_id' => $this->maps['company']->id, 'company_id' => $this->maps['company']->id,
'name' => $this->getString($data, 'client.name'), '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'), 'address1' => $this->getString($data, 'client.address1'),
'address2' => $this->getString($data, 'client.address2'), 'address2' => $this->getString($data, 'client.address2'),
'city' => $this->getString($data, 'client.city'), 'city' => $this->getString($data, 'client.city'),

View File

@ -37,7 +37,7 @@ class ClientTransformer extends BaseTransformer
return [ return [
'company_id' => $this->maps['company']->id, 'company_id' => $this->maps['company']->id,
'name' => $this->getString($data, 'client.name'), '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'), 'address1' => $this->getString($data, 'client.address1'),
'address2' => $this->getString($data, 'client.address2'), 'address2' => $this->getString($data, 'client.address2'),
'postal_code' => $this->getString($data, 'client.postal_code'), 'postal_code' => $this->getString($data, 'client.postal_code'),

View File

@ -34,7 +34,7 @@ class ClientTransformer extends BaseTransformer
return [ return [
'company_id' => $this->maps['company']->id, 'company_id' => $this->maps['company']->id,
'name' => $this->getString($data, 'Organization'), 'name' => $this->getString($data, 'Organization'),
'work_phone' => $this->getString($data, 'Phone'), 'phone' => $this->getString($data, 'Phone'),
'address1' => $this->getString($data, 'Street'), 'address1' => $this->getString($data, 'Street'),
'city' => $this->getString($data, 'City'), 'city' => $this->getString($data, 'City'),
'state' => $this->getString($data, 'Province/State'), 'state' => $this->getString($data, 'Province/State'),

View File

@ -34,7 +34,7 @@ class ClientTransformer extends BaseTransformer
return [ return [
'company_id' => $this->maps['company']->id, 'company_id' => $this->maps['company']->id,
'name' => $this->getString($data, 'Client Name'), '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, 'country_id' => isset($data['Country']) ? $this->getCountryIdBy2($data['Country']) : null,
'credit_balance' => 0, 'credit_balance' => 0,
'settings' => new \stdClass, 'settings' => new \stdClass,

View File

@ -42,7 +42,7 @@ class ClientTransformer extends BaseTransformer
'company_id' => $this->maps['company']->id, 'company_id' => $this->maps['company']->id,
'name' => $this->getString($data, 'customer_name'), 'name' => $this->getString($data, 'customer_name'),
'number' => $this->getString($data, 'account_number'), 'number' => $this->getString($data, 'account_number'),
'work_phone' => $this->getString($data, 'phone'), 'phone' => $this->getString($data, 'phone'),
'website' => $this->getString($data, 'website'), 'website' => $this->getString($data, 'website'),
'country_id' => ! empty($data['country']) ? $this->getCountryId($data['country']) : null, 'country_id' => ! empty($data['country']) ? $this->getCountryId($data['country']) : null,
'state' => $this->getString($data, 'province/state'), 'state' => $this->getString($data, 'province/state'),

View File

@ -41,7 +41,7 @@ class ClientTransformer extends BaseTransformer
return [ return [
'company_id' => $this->maps['company']->id, 'company_id' => $this->maps['company']->id,
'name' => $this->getString($data, 'Company Name'), 'name' => $this->getString($data, 'Company Name'),
'work_phone' => $this->getString($data, 'Phone'), 'phone' => $this->getString($data, 'Phone'),
'private_notes' => $this->getString($data, 'Notes'), 'private_notes' => $this->getString($data, 'Notes'),
'website' => $this->getString($data, 'Website'), 'website' => $this->getString($data, 'Website'),
'id_number' => $this->getString($data, 'Customer ID'), 'id_number' => $this->getString($data, 'Customer ID'),

View File

@ -266,7 +266,7 @@ class MatchBankTransactions implements ShouldQueue
/* Create Payment */ /* Create Payment */
$payment = PaymentFactory::create($this->invoice->company_id, $this->invoice->user_id); $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->applied = $this->applied_amount;
$payment->status_id = Payment::STATUS_COMPLETED; $payment->status_id = Payment::STATUS_COMPLETED;
$payment->client_id = $this->invoice->client_id; $payment->client_id = $this->invoice->client_id;
@ -315,7 +315,7 @@ class MatchBankTransactions implements ShouldQueue
$this->invoice $this->invoice
->client ->client
->service() ->service()
->updateBalanceAndPaidToDate($amount*-1, $amount) ->updateBalanceAndPaidToDate($this->applied_amount*-1, $amount)
->save(); ->save();
$this->invoice = $this->invoice $this->invoice = $this->invoice

View File

@ -11,6 +11,7 @@
namespace App\Mail\Engine; namespace App\Mail\Engine;
use App\Jobs\Entity\CreateRawPdf;
use App\Models\Account; use App\Models\Account;
use App\Utils\HtmlEngine; use App\Utils\HtmlEngine;
use App\Utils\Ninja; use App\Utils\Ninja;
@ -117,11 +118,17 @@ class CreditEmailEngine extends BaseEmailEngine
->setTextBody($text_body); ->setTextBody($text_body);
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->credit->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { if ($this->client->getSetting('pdf_email_attachment') !== false && $this->credit->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
if (Ninja::isHosted()) { // if (Ninja::isHosted()) {
$this->setAttachments([$this->credit->pdf_file_path($this->invitation, 'url', true)]); // $this->setAttachments([$this->credit->pdf_file_path($this->invitation, 'url', true)]);
} else { // } else {
$this->setAttachments([$this->credit->pdf_file_path($this->invitation)]); // $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 //attach third party documents
@ -129,11 +136,11 @@ class CreditEmailEngine extends BaseEmailEngine
// Storage::url // Storage::url
foreach ($this->credit->documents as $document) { 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) { 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())]]);
} }
} }

View File

@ -13,6 +13,7 @@ namespace App\Mail\Engine;
use App\DataMapper\EmailTemplateDefaults; use App\DataMapper\EmailTemplateDefaults;
use App\Jobs\Entity\CreateEntityPdf; use App\Jobs\Entity\CreateEntityPdf;
use App\Jobs\Entity\CreateRawPdf;
use App\Models\Account; use App\Models\Account;
use App\Models\Expense; use App\Models\Expense;
use App\Models\Task; use App\Models\Task;
@ -126,11 +127,15 @@ class InvoiceEmailEngine extends BaseEmailEngine
->setTextBody($text_body); ->setTextBody($text_body);
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->invoice->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { if ($this->client->getSetting('pdf_email_attachment') !== false && $this->invoice->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
if (Ninja::isHosted()) { // if (Ninja::isHosted()) {
$this->setAttachments([$this->invoice->pdf_file_path($this->invitation, 'url', true)]); // $this->setAttachments([$this->invoice->pdf_file_path($this->invitation, 'url', true)]);
} else { // } else {
$this->setAttachments([$this->invoice->pdf_file_path($this->invitation)]); // $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 //attach third party documents
@ -138,11 +143,11 @@ class InvoiceEmailEngine extends BaseEmailEngine
// Storage::url // Storage::url
foreach ($this->invoice->documents as $document) { 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) { 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; $line_items = $this->invoice->line_items;
@ -160,7 +165,7 @@ class InvoiceEmailEngine extends BaseEmailEngine
->cursor() ->cursor()
->each(function ($expense) { ->each(function ($expense) {
foreach ($expense->documents as $document) { 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() ->cursor()
->each(function ($task) { ->each(function ($task) {
foreach ($task->documents as $document) { 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, ]]);
} }
}); });
} }

View File

@ -12,6 +12,7 @@
namespace App\Mail\Engine; namespace App\Mail\Engine;
use App\DataMapper\EmailTemplateDefaults; use App\DataMapper\EmailTemplateDefaults;
use App\Jobs\Entity\CreateRawPdf;
use App\Models\Account; use App\Models\Account;
use App\Utils\Helpers; use App\Utils\Helpers;
use App\Utils\Ninja; 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)) { if ($this->client->getSetting('pdf_email_attachment') !== false && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
$this->payment->invoices->each(function ($invoice) { $this->payment->invoices->each(function ($invoice) {
if (Ninja::isHosted()) { // if (Ninja::isHosted()) {
$this->setAttachments([$invoice->pdf_file_path($invoice->invitations->first(), 'url', true)]); // $this->setAttachments([$invoice->pdf_file_path($invoice->invitations->first(), 'url', true)]);
} else { // } else {
$this->setAttachments([$invoice->pdf_file_path($invoice->invitations->first())]); // $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']]);
}); });
} }

View File

@ -13,6 +13,7 @@ namespace App\Mail\Engine;
use App\DataMapper\EmailTemplateDefaults; use App\DataMapper\EmailTemplateDefaults;
use App\Jobs\Entity\CreateEntityPdf; use App\Jobs\Entity\CreateEntityPdf;
use App\Jobs\Vendor\CreatePurchaseOrderPdf;
use App\Models\Account; use App\Models\Account;
use App\Models\Expense; use App\Models\Expense;
use App\Models\PurchaseOrder; use App\Models\PurchaseOrder;
@ -125,11 +126,16 @@ class PurchaseOrderEmailEngine extends BaseEmailEngine
->setTextBody($text_body); ->setTextBody($text_body);
if ($this->vendor->getSetting('pdf_email_attachment') !== false && $this->purchase_order->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { if ($this->vendor->getSetting('pdf_email_attachment') !== false && $this->purchase_order->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
if (Ninja::isHosted()) { // if (Ninja::isHosted()) {
$this->setAttachments([$this->purchase_order->pdf_file_path($this->invitation, 'url', true)]); // $this->setAttachments([$this->purchase_order->pdf_file_path($this->invitation, 'url', true)]);
} else { // } else {
$this->setAttachments([$this->purchase_order->pdf_file_path($this->invitation)]); // $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 //attach third party documents
@ -138,10 +144,12 @@ class PurchaseOrderEmailEngine extends BaseEmailEngine
// Storage::url // Storage::url
foreach ($this->purchase_order->documents as $document) { 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]]);
// $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, 'file' => base64_encode($document->getFile())]]);
} }
foreach ($this->purchase_order->company->documents as $document) { 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]]);
// $this->setAttachments([['path' => $document->filePath(), 'name' => $document->name, 'mime' => NULL, 'file' => base64_encode($document->getFile())]]);
} }
} }

View File

@ -11,6 +11,7 @@
namespace App\Mail\Engine; namespace App\Mail\Engine;
use App\Jobs\Entity\CreateRawPdf;
use App\Models\Account; use App\Models\Account;
use App\Utils\HtmlEngine; use App\Utils\HtmlEngine;
use App\Utils\Ninja; use App\Utils\Ninja;
@ -116,11 +117,15 @@ class QuoteEmailEngine extends BaseEmailEngine
->setTextBody($text_body); ->setTextBody($text_body);
if ($this->client->getSetting('pdf_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) { if ($this->client->getSetting('pdf_email_attachment') !== false && $this->quote->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
if (Ninja::isHosted()) { // if (Ninja::isHosted()) {
$this->setAttachments([$this->quote->pdf_file_path($this->invitation, 'url', true)]); // $this->setAttachments([$this->quote->pdf_file_path($this->invitation, 'url', true)]);
} else { // } else {
$this->setAttachments([$this->quote->pdf_file_path($this->invitation)]); // $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 //attach third party documents
@ -128,11 +133,11 @@ class QuoteEmailEngine extends BaseEmailEngine
// Storage::url // Storage::url
foreach ($this->quote->documents as $document) { 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) { 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, ]]);
} }
} }

View File

@ -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.*/ /*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) // if($this->invitation->invoice)
$path = $this->client->invoice_filepath($this->invitation).$this->invitation->invoice->numberFormatter().'.pdf'; // $path = $this->client->invoice_filepath($this->invitation).$this->invitation->invoice->numberFormatter().'.pdf';
elseif($this->invitation->quote) // elseif($this->invitation->quote)
$path = $this->client->quote_filepath($this->invitation).$this->invitation->quote->numberFormatter().'.pdf'; // $path = $this->client->quote_filepath($this->invitation).$this->invitation->quote->numberFormatter().'.pdf';
elseif($this->invitation->credit) // elseif($this->invitation->credit)
$path = $this->client->credit_filepath($this->invitation).$this->invitation->credit->numberFormatter().'.pdf'; // $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)) { // if(!Storage::disk(config('filesystems.default'))->exists($path)) {
(new CreateEntityPdf($this->invitation))->handle(); // (new CreateEntityPdf($this->invitation))->handle();
sleep(2); // 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 //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) { foreach ($this->build_email->getAttachments() as $file) {
if (is_string($file)) { // if (is_string($file)) {
// nlog($file); // // nlog($file);
// $file_data = file_get_contents($file); // // $file_data = file_get_contents($file);
// $this->attachData($file_data, basename($file)); // // $this->attachData($file_data, basename($file));
$this->attach($file); // $this->attach($file);
} elseif (is_array($file)) { // } elseif (is_array($file)) {
// nlog($file['path']); // // nlog($file['path']);
// $file_data = file_get_contents($file['path']); // // $file_data = file_get_contents($file['path']);
// $this->attachData($file_data, $file['name']); // // $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]); $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)) { if ($this->invitation && $this->invitation->invoice && $settings->ubl_email_attachment && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {

View File

@ -110,40 +110,39 @@ class VendorTemplateEmail extends Mailable
'whitelabel' => $this->vendor->user->account->isPaid() ? true : false, 'whitelabel' => $this->vendor->user->account->isPaid() ? true : false,
'logo' => $this->company->present()->logo($settings), '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 = false;
$path = $this->vendor->purchase_order_filepath($this->invitation).$this->invitation->purchase_order->numberFormatter().'.pdf';
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)) { // sleep(2);
(new CreatePurchaseOrderPdf($this->invitation))->handle();
sleep(2);
}
} // if(!Storage::disk(config('filesystems.default'))->exists($path)) {
// (new CreatePurchaseOrderPdf($this->invitation))->handle();
// sleep(2);
// }
} // }
// }
foreach ($this->build_email->getAttachments() as $file) { foreach ($this->build_email->getAttachments() as $file) {
if (is_string($file)) { // if (is_string($file)) {
$this->attach($file); // $this->attach($file);
} elseif (is_array($file)) { // } elseif (is_array($file)) {
$this->attach($file['path'], ['as' => $file['name'], 'mime' => null]); // $this->attach($file['path'], ['as' => $file['name'], 'mime' => null]);
} // }
$this->attachData(base64_decode($file['file']), $file['name']);
} }
return $this; return $this;

View File

@ -119,7 +119,7 @@ class CompanyPresenter extends EntityPresenter
$str .= e($country->name).'<br/>'; $str .= e($country->name).'<br/>';
} }
if ($settings->phone) { if ($settings->phone) {
$str .= ctrans('texts.work_phone').': '.e($settings->phone).'<br/>'; $str .= ctrans('texts.phone').': '.e($settings->phone).'<br/>';
} }
if ($settings->email) { if ($settings->email) {
$str .= ctrans('texts.work_email').': '.e($settings->email).'<br/>'; $str .= ctrans('texts.work_email').': '.e($settings->email).'<br/>';

View File

@ -94,8 +94,6 @@ class CreditCard implements MethodInterface
$customerRequest = $this->checkout->getCustomer(); $customerRequest = $this->checkout->getCustomer();
nlog($customerRequest);
$request = $this->bootRequest($gateway_response->token); $request = $this->bootRequest($gateway_response->token);
$request->capture = false; $request->capture = false;
$request->reference = '$1 payment for authorization.'; $request->reference = '$1 payment for authorization.';

View File

@ -34,6 +34,7 @@ use Checkout\CheckoutArgumentException;
use Checkout\CheckoutAuthorizationException; use Checkout\CheckoutAuthorizationException;
use Checkout\CheckoutDefaultSdk; use Checkout\CheckoutDefaultSdk;
use Checkout\CheckoutFourSdk; use Checkout\CheckoutFourSdk;
use Checkout\Common\Phone;
use Checkout\Customers\CustomerRequest; use Checkout\Customers\CustomerRequest;
use Checkout\Customers\Four\CustomerRequest as FourCustomerRequest; use Checkout\Customers\Four\CustomerRequest as FourCustomerRequest;
use Checkout\Environment; use Checkout\Environment;
@ -300,9 +301,12 @@ class CheckoutComPaymentDriver extends BaseDriver
$request = new CustomerRequest(); $request = new CustomerRequest();
} }
$phone = new Phone();
$phone->number = $this->client->present()->phone();
$request->email = $this->client->present()->email(); $request->email = $this->client->present()->email();
$request->name = $this->client->present()->name(); $request->name = $this->client->present()->name();
$request->phone = $this->client->present()->phone(); $request->phone = $phone;
try { try {
$response = $this->gateway->getCustomersClient()->create($request); $response = $this->gateway->getCustomersClient()->create($request);

View File

@ -36,6 +36,7 @@ use Stripe\Exception\CardException;
use Stripe\Exception\InvalidRequestException; use Stripe\Exception\InvalidRequestException;
use Stripe\Exception\RateLimitException; use Stripe\Exception\RateLimitException;
use Stripe\PaymentIntent; use Stripe\PaymentIntent;
use App\Utils\Number;
class ACH class ACH
{ {
@ -172,9 +173,9 @@ class ACH
->first(); ->first();
if ($invoice) { 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 { } 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(); ->first();
if ($invoice) { 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 { } 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') { if (substr($cgt->token, 0, 2) === 'pm') {
@ -454,9 +455,9 @@ class ACH
->first(); ->first();
if ($invoice) { 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 { } 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') { if (substr($source->token, 0, 2) === 'pm') {

View File

@ -32,6 +32,7 @@ use Stripe\Exception\CardException;
use Stripe\Exception\InvalidRequestException; use Stripe\Exception\InvalidRequestException;
use Stripe\Exception\RateLimitException; use Stripe\Exception\RateLimitException;
use Stripe\StripeClient; use Stripe\StripeClient;
use App\Utils\Number;
class Charge class Charge
{ {
@ -62,9 +63,9 @@ class Charge
$invoice = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->withTrashed()->first(); $invoice = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->withTrashed()->first();
if ($invoice) { 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 { } 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(); $this->stripe->init();

View File

@ -23,6 +23,7 @@ use App\PaymentDrivers\StripePaymentDriver;
use App\PaymentDrivers\Stripe\Jobs\UpdateCustomer; use App\PaymentDrivers\Stripe\Jobs\UpdateCustomer;
use Stripe\PaymentIntent; use Stripe\PaymentIntent;
use Stripe\PaymentMethod; use Stripe\PaymentMethod;
use App\Utils\Number;
class CreditCard 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()}"; // $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(','); $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 = [ $payment_intent_data = [
'amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()), 'amount' => $this->stripe->convertToStripeAmount($data['total']['amount_with_fee'], $this->stripe->client->currency()->precision, $this->stripe->client->currency()),

View File

@ -66,11 +66,9 @@ class PaymentIntentWebhook implements ShouldQueue
{ {
$payment = Payment::query() $payment = Payment::query()
->where('company_id', $company->id) ->where('company_id', $company->id)
->where(function ($query) use ($transaction) { ->where('transaction_reference', $transaction['payment_intent'])
$query->where('transaction_reference', $transaction['payment_intent'])
->orWhere('transaction_reference', $transaction['id']);
})
->first(); ->first();
} }
else else
{ {

View File

@ -56,6 +56,8 @@ class ClientRepository extends BaseRepository
*/ */
public function save(array $data, Client $client) : ?Client 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*/ /* When uploading documents, only the document array is sent, so we must return early*/
if (array_key_exists('documents', $data) && count($data['documents']) >= 1) { if (array_key_exists('documents', $data) && count($data['documents']) >= 1) {
@ -67,7 +69,7 @@ class ClientRepository extends BaseRepository
$client->fill($data); $client->fill($data);
if (array_key_exists('settings', $data)) { if (array_key_exists('settings', $data)) {
$client->saveSettings($data['settings'], $client); $client->settings = $client->saveSettings($data['settings'], $client);
} }
if (! $client->country_id) { if (! $client->country_id) {
@ -75,19 +77,9 @@ class ClientRepository extends BaseRepository
$client->country_id = $company->settings->country_id; $client->country_id = $company->settings->country_id;
} }
try{
$client->save(); $client->save();
}
catch(\Exception $e) {
nlog("client save failed");
nlog($data);
}
if (! isset($client->number) || empty($client->number) || strlen($client->number) == 0) { if (! isset($client->number) || empty($client->number) || strlen($client->number) == 0) {
// $client->number = $this->getNextClientNumber($client);
// $client->save();
$x = 1; $x = 1;
@ -111,7 +103,7 @@ class ClientRepository extends BaseRepository
$data['name'] = $client->present()->name(); $data['name'] = $client->present()->name();
} }
$this->contact_repo->save($data, $client); $this->contact_repo->save($contact_data, $client);
return $client; return $client;
} }

View File

@ -44,11 +44,12 @@ class HandleRestore extends AbstractService
return $this->invoice; 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) { 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 //adjust ledger balance
@ -56,8 +57,7 @@ class HandleRestore extends AbstractService
$this->invoice->client $this->invoice->client
->service() ->service()
->updateBalance($this->invoice->balance) ->updateBalanceAndPaidToDate($this->invoice->balance,$this->invoice->paid_to_date)
->updatePaidToDate($this->invoice->paid_to_date)
->save(); ->save();
$this->windBackInvoiceNumber(); $this->windBackInvoiceNumber();
@ -120,11 +120,11 @@ class HandleRestore extends AbstractService
if ($this->adjustment_amount == $this->total_payments) { if ($this->adjustment_amount == $this->total_payments) {
$this->invoice->payments()->update(['payments.deleted_at' => null, 'payments.is_deleted' => false]); $this->invoice->payments()->update(['payments.deleted_at' => null, 'payments.is_deleted' => false]);
} else { }
//adjust payments down by the amount applied to the invoice payment. //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 $payment_adjustment = $payment->paymentables
->where('paymentable_type', '=', 'invoices') ->where('paymentable_type', '=', 'invoices')
->where('paymentable_id', $this->invoice->id) ->where('paymentable_id', $this->invoice->id)
@ -141,7 +141,6 @@ class HandleRestore extends AbstractService
$payment->restore(); $payment->restore();
$payment->save(); $payment->save();
}); });
}
return $this; return $this;
} }

View File

@ -53,16 +53,6 @@ class MarkInvoiceDeleted extends AbstractService
->adjustPaidToDateAndBalance() ->adjustPaidToDateAndBalance()
->adjustLedger(); ->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; return $this->invoice;
} }
@ -87,26 +77,17 @@ class MarkInvoiceDeleted extends AbstractService
return $this; 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 */ /* Adjust the payment amounts */
private function adjustPayments() private function adjustPayments()
{ {
//if total payments = adjustment amount - that means we need to delete the payments as well. //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]); $this->invoice->payments()->update(['payments.deleted_at' => now(), 'payments.is_deleted' => true]);
} else {
//adjust payments down by the amount applied to the invoice payment. //adjust payments down by the amount applied to the invoice payment.
@ -125,7 +106,7 @@ class MarkInvoiceDeleted extends AbstractService
$payment->applied -= $payment_adjustment; $payment->applied -= $payment_adjustment;
$payment->save(); $payment->save();
}); });
}
return $this; return $this;
} }

View File

@ -28,6 +28,6 @@ class NinjaPdf
RequestOptions::JSON => ['html' => $html], RequestOptions::JSON => ['html' => $html],
]); ]);
return $response->getBody(); return $response->getBody()->getContents();
} }
} }

View File

@ -30,7 +30,7 @@ trait ClientGroupSettingsSaver
* Saves a setting object. * Saves a setting object.
* *
* Works for groups|clients|companies * 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 * @param object $entity The entity which the settings belongs to
* @return void * @return void
*/ */
@ -64,19 +64,6 @@ trait ClientGroupSettingsSaver
$entity_settings->{$key} = $value; $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; return $entity_settings;
} }

View File

@ -14,8 +14,8 @@ return [
'require_https' => env('REQUIRE_HTTPS', true), 'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
'app_version' => '5.5.43', 'app_version' => '5.5.44',
'app_tag' => '5.5.43', 'app_tag' => '5.5.44',
'minimum_client_version' => '5.0.16', 'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1', 'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', ''), 'api_secret' => env('API_SECRET', ''),

View File

@ -254,6 +254,8 @@ $LANG = array(
'notification_invoice_paid' => 'A payment of :amount was made by client :client towards Invoice :invoice.', '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_sent' => 'The following client :client was emailed Invoice :invoice for :amount.',
'notification_invoice_viewed' => 'The following client :client viewed 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:', 'reset_password' => 'You can reset your account password by clicking the following button:',
'secure_payment' => 'Secure Payment', 'secure_payment' => 'Secure Payment',
'card_number' => 'Card Number', 'card_number' => 'Card Number',
@ -4061,7 +4063,7 @@ $LANG = array(
'save_payment_method_details' => 'Save payment method details', 'save_payment_method_details' => 'Save payment method details',
'new_card' => 'New card', 'new_card' => 'New card',
'new_bank_account' => 'New bank account', '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', 'credits_applied_validation' => 'Total credits applied cannot be MORE than total of invoices',
'credit_number_taken' => 'Credit number already taken', 'credit_number_taken' => 'Credit number already taken',
'credit_not_found' => 'Credit not found', 'credit_not_found' => 'Credit not found',

View File

@ -11,12 +11,20 @@
namespace Tests\Feature; 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\Models\Country;
use App\Repositories\ClientContactRepository;
use App\Repositories\ClientRepository;
use App\Utils\Number; use App\Utils\Number;
use App\Utils\Traits\ClientGroupSettingsSaver;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Session; use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
use Tests\MockAccountData; use Tests\MockAccountData;
use Tests\TestCase; use Tests\TestCase;
@ -30,6 +38,7 @@ class ClientApiTest extends TestCase
use MakesHash; use MakesHash;
use DatabaseTransactions; use DatabaseTransactions;
use MockAccountData; use MockAccountData;
use ClientGroupSettingsSaver;
protected function setUp() :void protected function setUp() :void
{ {
@ -44,6 +53,302 @@ class ClientApiTest extends TestCase
Model::reguard(); 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() public function testIllegalPropertiesInClientSettings()
{ {
$settings = [ $settings = [

View File

@ -162,6 +162,7 @@ class DeleteInvoiceTest extends TestCase
$payment = $payment->fresh(); $payment = $payment->fresh();
$this->assertTrue($payment->is_deleted); $this->assertTrue($payment->is_deleted);
$this->assertEquals(0, $payment->amount);
$this->assertEquals(4, $payment->status_id); $this->assertEquals(4, $payment->status_id);
$client->fresh(); $client->fresh();