public function sendInvoice(Invoice $invoice, $reminder = false) { $invoice->load('invitations', 'client.language', 'account'); $entityType = $invoice->getEntityType(); $client = $invoice->client; $account = $invoice->account; if ($invoice->trashed() || $client->trashed()) { return false; } $account->loadLocalizationSettings($client); if ($account->pdf_email_attachment) { $invoice->updateCachedPDF(); } $view = 'invoice'; $accountName = $invoice->account->getDisplayName(); $emailTemplate = $invoice->account->getEmailTemplate($reminder ?: $entityType); $emailSubject = $invoice->account->getEmailSubject($reminder ?: $entityType); $this->initClosure($invoice); $response = false; $sent = false; foreach ($invoice->invitations as $invitation) { if (Auth::check()) { $user = Auth::user(); } else { $user = $invitation->user; if ($invitation->user->trashed()) { $user = $account->users()->orderBy('id')->first(); } } if (!$user->email || !$user->confirmed) { continue; } if (!$invitation->contact->email || $invitation->contact->trashed()) { continue; } $invitation->sent_date = \Carbon::now()->toDateTimeString(); $invitation->save(); $variables = ['account' => $account, 'client' => $client, 'invitation' => $invitation, 'amount' => $invoice->getRequestedAmount()]; $data['body'] = $this->processVariables($emailTemplate, $variables); $data['link'] = $invitation->getLink(); $data['entityType'] = $entityType; $data['invoice_id'] = $invoice->id; $subject = $this->processVariables($emailSubject, $variables); $fromEmail = $user->email; $response = $this->sendTo($invitation->contact->email, $fromEmail, $accountName, $subject, $view, $data); if ($response === true) { $sent = true; Activity::emailInvoice($invitation); } } if ($sent === true) { if (!$invoice->isSent()) { $invoice->invoice_status_id = INVOICE_STATUS_SENT; $invoice->save(); } $account->loadLocalizationSettings(); Event::fire(new InvoiceSent($invoice)); } return $response ?: trans('texts.email_error'); }
public function sendInvoice(Invoice $invoice, $reminder = false, $pdfString = false) { $invoice->load('invitations', 'client.language', 'account'); $entityType = $invoice->getEntityType(); $client = $invoice->client; $account = $invoice->account; if ($client->trashed()) { return trans('texts.email_errors.inactive_client'); } elseif ($invoice->trashed()) { return trans('texts.email_errors.inactive_invoice'); } $account->loadLocalizationSettings($client); $emailTemplate = $account->getEmailTemplate($reminder ?: $entityType); $emailSubject = $account->getEmailSubject($reminder ?: $entityType); $sent = false; if ($account->attatchPDF() && !$pdfString) { $pdfString = $invoice->getPDFString(); } foreach ($invoice->invitations as $invitation) { $response = $this->sendInvitation($invitation, $invoice, $emailTemplate, $emailSubject, $pdfString); if ($response === true) { $sent = true; } } $account->loadLocalizationSettings(); if ($sent === true) { if ($invoice->is_quote) { event(new QuoteWasEmailed($invoice)); } else { event(new InvoiceWasEmailed($invoice)); } } return $response; }
public function sendInvoice(Invoice $invoice, $reminder = false, $pdfString = false) { $invoice->load('invitations', 'client.language', 'account'); $entityType = $invoice->getEntityType(); $client = $invoice->client; $account = $invoice->account; if ($invoice->trashed() || $client->trashed()) { return false; } $account->loadLocalizationSettings($client); $emailTemplate = $account->getEmailTemplate($reminder ?: $entityType); $emailSubject = $account->getEmailSubject($reminder ?: $entityType); $sent = false; if ($account->attatchPDF() && !$pdfString) { $pdfString = $invoice->getPDFString(); } foreach ($invoice->invitations as $invitation) { if ($this->sendInvitation($invitation, $invoice, $emailTemplate, $emailSubject, $pdfString)) { $sent = true; } } $account->loadLocalizationSettings(); if ($sent === true) { Event::fire(new InvoiceSent($invoice)); } return $sent ?: trans('texts.email_error'); }
public function sendInvoice(Invoice $invoice) { $invoice->load('invitations', 'client', 'account'); $entityType = $invoice->getEntityType(); $view = 'invoice'; $subject = trans("texts.{$entityType}_subject", ['invoice' => $invoice->invoice_number, 'account' => $invoice->account->getDisplayName()]); $accountName = $invoice->account->getDisplayName(); $emailTemplate = $invoice->account->getEmailTemplate($entityType); $invoiceAmount = Utils::formatMoney($invoice->getRequestedAmount(), $invoice->client->currency_id); foreach ($invoice->invitations as $invitation) { if (!$invitation->user || !$invitation->user->email) { return false; } if (!$invitation->contact || !$invitation->contact->email) { return false; } $invitation->sent_date = \Carbon::now()->toDateTimeString(); $invitation->save(); $variables = ['$footer' => $invoice->account->getEmailFooter(), '$link' => $invitation->getLink(), '$client' => $invoice->client->getDisplayName(), '$account' => $accountName, '$contact' => $invitation->contact->getDisplayName(), '$amount' => $invoiceAmount]; $data['body'] = str_replace(array_keys($variables), array_values($variables), $emailTemplate); $data['link'] = $invitation->getLink(); $data['entityType'] = $entityType; $data['invoice_id'] = $invoice->id; $fromEmail = $invitation->user->email; $this->sendTo($invitation->contact->email, $fromEmail, $accountName, $subject, $view, $data); Activity::emailInvoice($invitation); } if (!$invoice->isSent()) { $invoice->invoice_status_id = INVOICE_STATUS_SENT; $invoice->save(); } Event::fire(new InvoiceSent($invoice)); }
public function sendInvoice(Invoice $invoice, $reminder = false, $pdfString = false) { $invoice->load('invitations', 'client.language', 'account'); $entityType = $invoice->getEntityType(); $client = $invoice->client; $account = $invoice->account; $response = null; if ($client->trashed()) { return trans('texts.email_errors.inactive_client'); } elseif ($invoice->trashed()) { return trans('texts.email_errors.inactive_invoice'); } $account->loadLocalizationSettings($client); $emailTemplate = $account->getEmailTemplate($reminder ?: $entityType); $emailSubject = $account->getEmailSubject($reminder ?: $entityType); $sent = false; if ($account->attatchPDF() && !$pdfString) { $pdfString = $invoice->getPDFString(); } $documentStrings = array(); if ($account->document_email_attachment && $invoice->hasDocuments()) { $documents = $invoice->documents; foreach ($invoice->expenses as $expense) { $documents = $documents->merge($expense->documents); } $documents = $documents->sortBy('size'); $size = 0; $maxSize = MAX_EMAIL_DOCUMENTS_SIZE * 1000; foreach ($documents as $document) { $size += $document->size; if ($size > $maxSize) { break; } $documentStrings[] = array('name' => $document->name, 'data' => $document->getRaw()); } } foreach ($invoice->invitations as $invitation) { $response = $this->sendInvitation($invitation, $invoice, $emailTemplate, $emailSubject, $pdfString, $documentStrings); if ($response === true) { $sent = true; } } $account->loadLocalizationSettings(); if ($sent === true) { if ($invoice->is_quote) { event(new QuoteWasEmailed($invoice)); } else { event(new InvoiceWasEmailed($invoice)); } } return $response; }
public function sendNotification(User $user, Invoice $invoice, $notificationType, Payment $payment = null) { if (!$user->email) { return; } $entityType = $invoice->getEntityType(); $view = ($notificationType == 'approved' ? ENTITY_QUOTE : ENTITY_INVOICE) . "_{$notificationType}"; $account = $user->account; $client = $invoice->client; $data = ['entityType' => $entityType, 'clientName' => $client->getDisplayName(), 'accountName' => $account->getDisplayName(), 'userName' => $user->getDisplayName(), 'invoiceAmount' => $account->formatMoney($invoice->getRequestedAmount(), $client), 'invoiceNumber' => $invoice->invoice_number, 'invoiceLink' => SITE_URL . "/{$entityType}s/{$invoice->public_id}", 'account' => $account]; if ($payment) { $data['paymentAmount'] = $account->formatMoney($payment->amount, $client); } $subject = trans("texts.notification_{$entityType}_{$notificationType}_subject", ['invoice' => $invoice->invoice_number, 'client' => $client->getDisplayName()]); $this->sendTo($user->email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data); }
public function sendInvoice(Invoice $invoice) { $invoice->load('invitations', 'client.language', 'account'); $entityType = $invoice->getEntityType(); $client = $invoice->client; $account = $invoice->account; $account->loadLocalizationSettings($client); $view = 'invoice'; $subject = trans("texts.{$entityType}_subject", ['invoice' => $invoice->invoice_number, 'account' => $invoice->account->getDisplayName()]); $accountName = $invoice->account->getDisplayName(); $emailTemplate = $invoice->account->getEmailTemplate($entityType); $invoiceAmount = Utils::formatMoney($invoice->getRequestedAmount(), $client->getCurrencyId()); $this->initClosure($invoice); foreach ($invoice->invitations as $invitation) { if (!$invitation->user || !$invitation->user->email || $invitation->user->trashed()) { return false; } if (!$invitation->contact || !$invitation->contact->email || $invitation->contact->trashed()) { return false; } $invitation->sent_date = \Carbon::now()->toDateTimeString(); $invitation->save(); $variables = ['$footer' => $invoice->account->getEmailFooter(), '$link' => $invitation->getLink(), '$client' => $client->getDisplayName(), '$account' => $accountName, '$contact' => $invitation->contact->getDisplayName(), '$amount' => $invoiceAmount, '$advancedRawInvoice->' => '$']; // Add variables for available payment types foreach (Gateway::getPaymentTypeLinks() as $type) { $variables["\${$type}_link"] = URL::to("/payment/{$invitation->invitation_key}/{$type}"); } $data['body'] = str_replace(array_keys($variables), array_values($variables), $emailTemplate); $data['body'] = preg_replace_callback('/\\{\\{\\$?(.*)\\}\\}/', $this->advancedTemplateHandler, $data['body']); $data['link'] = $invitation->getLink(); $data['entityType'] = $entityType; $data['invoice_id'] = $invoice->id; $fromEmail = $invitation->user->email; $response = $this->sendTo($invitation->contact->email, $fromEmail, $accountName, $subject, $view, $data); if ($response !== true) { return $response; } Activity::emailInvoice($invitation); } if (!$invoice->isSent()) { $invoice->invoice_status_id = INVOICE_STATUS_SENT; $invoice->save(); } $account->loadLocalizationSettings(); Event::fire(new InvoiceSent($invoice)); return $response; }
/** * @param array $data * @param Invoice|null $invoice * @return Invoice|mixed */ public function save(array $data, Invoice $invoice = null) { /** @var Account $account */ $account = \Auth::user()->account; $publicId = isset($data['public_id']) ? $data['public_id'] : false; $isNew = !$publicId || $publicId == '-1'; if ($invoice) { // do nothing $entityType = $invoice->getEntityType(); } elseif ($isNew) { $entityType = ENTITY_INVOICE; if (isset($data['is_recurring']) && filter_var($data['is_recurring'], FILTER_VALIDATE_BOOLEAN)) { $entityType = ENTITY_RECURRING_INVOICE; } elseif (isset($data['is_quote']) && filter_var($data['is_quote'], FILTER_VALIDATE_BOOLEAN)) { $entityType = ENTITY_QUOTE; } $invoice = $account->createInvoice($entityType, $data['client_id']); $invoice->invoice_date = date_create()->format('Y-m-d'); if (isset($data['has_tasks']) && filter_var($data['has_tasks'], FILTER_VALIDATE_BOOLEAN)) { $invoice->has_tasks = true; } if (isset($data['has_expenses']) && filter_var($data['has_expenses'], FILTER_VALIDATE_BOOLEAN)) { $invoice->has_expenses = true; } } else { $invoice = Invoice::scope($publicId)->firstOrFail(); \Log::warning('Entity not set in invoice repo save'); } $invoice->fill($data); if (isset($data['set_default_terms']) && $data['set_default_terms'] || isset($data['set_default_footer']) && $data['set_default_footer']) { if (isset($data['set_default_terms']) && $data['set_default_terms']) { $account->{"{$invoice->getEntityType()}_terms"} = trim($data['terms']); } if (isset($data['set_default_footer']) && $data['set_default_footer']) { $account->invoice_footer = trim($data['invoice_footer']); } $account->save(); } if (isset($data['invoice_number']) && !$invoice->is_recurring) { $invoice->invoice_number = trim($data['invoice_number']); } if (isset($data['discount'])) { $invoice->discount = round(Utils::parseFloat($data['discount']), 2); } if (isset($data['is_amount_discount'])) { $invoice->is_amount_discount = $data['is_amount_discount'] ? true : false; } if (isset($data['partial'])) { $invoice->partial = round(Utils::parseFloat($data['partial']), 2); } if (isset($data['invoice_date_sql'])) { $invoice->invoice_date = $data['invoice_date_sql']; } elseif (isset($data['invoice_date'])) { $invoice->invoice_date = Utils::toSqlDate($data['invoice_date']); } if (isset($data['invoice_status_id'])) { if ($data['invoice_status_id'] == 0) { $data['invoice_status_id'] = INVOICE_STATUS_DRAFT; } $invoice->invoice_status_id = $data['invoice_status_id']; } if ($invoice->is_recurring) { if ($invoice->start_date && $invoice->start_date != Utils::toSqlDate($data['start_date'])) { $invoice->last_sent_date = null; } $invoice->frequency_id = $data['frequency_id'] ? $data['frequency_id'] : 0; $invoice->start_date = Utils::toSqlDate($data['start_date']); $invoice->end_date = Utils::toSqlDate($data['end_date']); $invoice->client_enable_auto_bill = isset($data['client_enable_auto_bill']) && $data['client_enable_auto_bill'] ? true : false; $invoice->auto_bill = isset($data['auto_bill']) ? intval($data['auto_bill']) : AUTO_BILL_OFF; if ($invoice->auto_bill < AUTO_BILL_OFF || $invoice->auto_bill > AUTO_BILL_ALWAYS) { $invoice->auto_bill = AUTO_BILL_OFF; } if (isset($data['recurring_due_date'])) { $invoice->due_date = $data['recurring_due_date']; } elseif (isset($data['due_date'])) { $invoice->due_date = $data['due_date']; } } else { if (isset($data['due_date']) || isset($data['due_date_sql'])) { $invoice->due_date = isset($data['due_date_sql']) ? $data['due_date_sql'] : Utils::toSqlDate($data['due_date']); } $invoice->frequency_id = 0; $invoice->start_date = null; $invoice->end_date = null; } if (isset($data['terms']) && trim($data['terms'])) { $invoice->terms = trim($data['terms']); } elseif ($isNew && $account->{"{$entityType}_terms"}) { $invoice->terms = $account->{"{$entityType}_terms"}; } else { $invoice->terms = ''; } $invoice->invoice_footer = isset($data['invoice_footer']) && trim($data['invoice_footer']) ? trim($data['invoice_footer']) : (!$publicId && $account->invoice_footer ? $account->invoice_footer : ''); $invoice->public_notes = isset($data['public_notes']) ? trim($data['public_notes']) : null; // process date variables if not recurring if (!$invoice->is_recurring) { $invoice->terms = Utils::processVariables($invoice->terms); $invoice->invoice_footer = Utils::processVariables($invoice->invoice_footer); $invoice->public_notes = Utils::processVariables($invoice->public_notes); } if (isset($data['po_number'])) { $invoice->po_number = trim($data['po_number']); } $invoice->invoice_design_id = isset($data['invoice_design_id']) ? $data['invoice_design_id'] : $account->invoice_design_id; // provide backwards compatability if (isset($data['tax_name']) && isset($data['tax_rate'])) { $data['tax_name1'] = $data['tax_name']; $data['tax_rate1'] = $data['tax_rate']; } $total = 0; $itemTax = 0; foreach ($data['invoice_items'] as $item) { $item = (array) $item; if (!$item['cost'] && !$item['product_key'] && !$item['notes']) { continue; } $invoiceItemCost = round(Utils::parseFloat($item['cost']), 2); $invoiceItemQty = round(Utils::parseFloat($item['qty']), 2); $lineTotal = $invoiceItemCost * $invoiceItemQty; $total += round($lineTotal, 2); } foreach ($data['invoice_items'] as $item) { $item = (array) $item; $invoiceItemCost = round(Utils::parseFloat($item['cost']), 2); $invoiceItemQty = round(Utils::parseFloat($item['qty']), 2); $lineTotal = $invoiceItemCost * $invoiceItemQty; if ($invoice->discount > 0) { if ($invoice->is_amount_discount) { $lineTotal -= round($lineTotal / $total * $invoice->discount, 2); } else { $lineTotal -= round($lineTotal * ($invoice->discount / 100), 2); } } if (isset($item['tax_rate1']) && Utils::parseFloat($item['tax_rate1']) > 0) { $invoiceItemTaxRate = Utils::parseFloat($item['tax_rate1']); $itemTax += round($lineTotal * $invoiceItemTaxRate / 100, 2); } if (isset($item['tax_rate2']) && Utils::parseFloat($item['tax_rate2']) > 0) { $invoiceItemTaxRate = Utils::parseFloat($item['tax_rate2']); $itemTax += round($lineTotal * $invoiceItemTaxRate / 100, 2); } } if ($invoice->discount > 0) { if ($invoice->is_amount_discount) { $total -= $invoice->discount; } else { $total *= (100 - $invoice->discount) / 100; $total = round($total, 2); } } if (isset($data['custom_value1'])) { $invoice->custom_value1 = round($data['custom_value1'], 2); if ($isNew) { $invoice->custom_taxes1 = $account->custom_invoice_taxes1 ?: false; } } if (isset($data['custom_value2'])) { $invoice->custom_value2 = round($data['custom_value2'], 2); if ($isNew) { $invoice->custom_taxes2 = $account->custom_invoice_taxes2 ?: false; } } if (isset($data['custom_text_value1'])) { $invoice->custom_text_value1 = trim($data['custom_text_value1']); } if (isset($data['custom_text_value2'])) { $invoice->custom_text_value2 = trim($data['custom_text_value2']); } // custom fields charged taxes if ($invoice->custom_value1 && $invoice->custom_taxes1) { $total += $invoice->custom_value1; } if ($invoice->custom_value2 && $invoice->custom_taxes2) { $total += $invoice->custom_value2; } $taxAmount1 = round($total * $invoice->tax_rate1 / 100, 2); $taxAmount2 = round($total * $invoice->tax_rate2 / 100, 2); $total = round($total + $taxAmount1 + $taxAmount2, 2); $total += $itemTax; // custom fields not charged taxes if ($invoice->custom_value1 && !$invoice->custom_taxes1) { $total += $invoice->custom_value1; } if ($invoice->custom_value2 && !$invoice->custom_taxes2) { $total += $invoice->custom_value2; } if ($publicId) { $invoice->balance = $total - ($invoice->amount - $invoice->balance); } else { $invoice->balance = $total; } $invoice->amount = $total; $invoice->save(); if ($publicId) { $invoice->invoice_items()->forceDelete(); } $document_ids = !empty($data['document_ids']) ? array_map('intval', $data['document_ids']) : []; foreach ($document_ids as $document_id) { $document = Document::scope($document_id)->first(); if ($document && Auth::user()->can('edit', $document)) { if ($document->invoice_id && $document->invoice_id != $invoice->id) { // From a clone $document = $document->cloneDocument(); $document_ids[] = $document->public_id; // Don't remove this document } $document->invoice_id = $invoice->id; $document->expense_id = null; $document->save(); } } if (!$invoice->wasRecentlyCreated) { foreach ($invoice->documents as $document) { if (!in_array($document->public_id, $document_ids)) { // Removed // Not checking permissions; deleting a document is just editing the invoice if ($document->invoice_id == $invoice->id) { // Make sure the document isn't on a clone $document->delete(); } } } } foreach ($data['invoice_items'] as $item) { $item = (array) $item; if (empty($item['cost']) && empty($item['product_key']) && empty($item['notes']) && empty($item['custom_value1']) && empty($item['custom_value2'])) { continue; } $task = false; if (isset($item['task_public_id']) && $item['task_public_id']) { $task = Task::scope($item['task_public_id'])->where('invoice_id', '=', null)->firstOrFail(); if (Auth::user()->can('edit', $task)) { $task->invoice_id = $invoice->id; $task->client_id = $invoice->client_id; $task->save(); } } $expense = false; if (isset($item['expense_public_id']) && $item['expense_public_id']) { $expense = Expense::scope($item['expense_public_id'])->where('invoice_id', '=', null)->firstOrFail(); if (Auth::user()->can('edit', $expense)) { $expense->invoice_id = $invoice->id; $expense->client_id = $invoice->client_id; $expense->save(); } } if ($productKey = trim($item['product_key'])) { if (\Auth::user()->account->update_products && !$invoice->has_tasks && !$invoice->has_expenses) { $product = Product::findProductByKey($productKey); if (!$product) { if (Auth::user()->can('create', ENTITY_PRODUCT)) { $product = Product::createNew(); $product->product_key = trim($item['product_key']); } else { $product = null; } } if ($product && Auth::user()->can('edit', $product)) { $product->notes = $task || $expense ? '' : $item['notes']; $product->cost = $expense ? 0 : $item['cost']; $product->save(); } } } $invoiceItem = InvoiceItem::createNew(); $invoiceItem->product_id = isset($product) ? $product->id : null; $invoiceItem->product_key = isset($item['product_key']) ? trim($invoice->is_recurring ? $item['product_key'] : Utils::processVariables($item['product_key'])) : ''; $invoiceItem->notes = trim($invoice->is_recurring ? $item['notes'] : Utils::processVariables($item['notes'])); $invoiceItem->cost = Utils::parseFloat($item['cost']); $invoiceItem->qty = Utils::parseFloat($item['qty']); if (isset($item['custom_value1'])) { $invoiceItem->custom_value1 = $item['custom_value1']; } if (isset($item['custom_value2'])) { $invoiceItem->custom_value2 = $item['custom_value2']; } // provide backwards compatability if (isset($item['tax_name']) && isset($item['tax_rate'])) { $item['tax_name1'] = $item['tax_name']; $item['tax_rate1'] = $item['tax_rate']; } $invoiceItem->fill($item); $invoice->invoice_items()->save($invoiceItem); } return $invoice; }
/** * @param Invitation $invitation * @param Invoice $invoice * @param $body * @param $subject * @param $pdfString * @param $documentStrings * @return bool|string * @throws \Laracasts\Presenter\Exceptions\PresenterException */ private function sendInvitation(Invitation $invitation, Invoice $invoice, $body, $subject, $pdfString, $documentStrings) { $client = $invoice->client; $account = $invoice->account; if (Auth::check()) { $user = Auth::user(); } else { $user = $invitation->user; if ($invitation->user->trashed()) { $user = $account->users()->orderBy('id')->first(); } } if (!$user->email || !$user->registered) { return trans('texts.email_error_user_unregistered'); } elseif (!$user->confirmed) { return trans('texts.email_error_user_unconfirmed'); } elseif (!$invitation->contact->email) { return trans('texts.email_error_invalid_contact_email'); } elseif ($invitation->contact->trashed()) { return trans('texts.email_error_inactive_contact'); } $variables = ['account' => $account, 'client' => $client, 'invitation' => $invitation, 'amount' => $invoice->getRequestedAmount()]; // Let the client know they'll be billed later if ($client->autoBillLater()) { $variables['autobill'] = $invoice->present()->autoBillEmailMessage(); } if (empty($invitation->contact->password) && $account->hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD) && $account->enable_portal_password && $account->send_portal_password) { // The contact needs a password $variables['password'] = $password = $this->generatePassword(); $invitation->contact->password = bcrypt($password); $invitation->contact->save(); } $data = ['body' => $this->templateService->processVariables($body, $variables), 'link' => $invitation->getLink(), 'entityType' => $invoice->getEntityType(), 'invoiceId' => $invoice->id, 'invitation' => $invitation, 'account' => $account, 'client' => $client, 'invoice' => $invoice, 'documents' => $documentStrings]; if ($account->attachPDF()) { $data['pdfString'] = $pdfString; $data['pdfFileName'] = $invoice->getFileName(); } $subject = $this->templateService->processVariables($subject, $variables); $fromEmail = $user->email; $view = $account->getTemplateView(ENTITY_INVOICE); $response = $this->sendTo($invitation->contact->email, $fromEmail, $account->getDisplayName(), $subject, $view, $data); if ($response === true) { return true; } else { return $response; } }