mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-18 00:53:10 +01:00
Added auto-billing and per-client language support
This commit is contained in:
parent
3fdad48179
commit
4741fad4be
@ -41,8 +41,8 @@ class SendRecurringInvoices extends Command
|
|||||||
|
|
||||||
$invoice = $this->invoiceRepo->createRecurringInvoice($recurInvoice);
|
$invoice = $this->invoiceRepo->createRecurringInvoice($recurInvoice);
|
||||||
|
|
||||||
if ($invoice) {
|
if ($invoice && !$invoice->isPaid()) {
|
||||||
$recurInvoice->account->loadLocalizationSettings();
|
$recurInvoice->account->loadLocalizationSettings($invoice->client);
|
||||||
$this->mailer->sendInvoice($invoice);
|
$this->mailer->sendInvoice($invoice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ use App\Ninja\Repositories\AccountRepository;
|
|||||||
use App\Ninja\Mailers\UserMailer;
|
use App\Ninja\Mailers\UserMailer;
|
||||||
use App\Ninja\Mailers\ContactMailer;
|
use App\Ninja\Mailers\ContactMailer;
|
||||||
use App\Events\UserLoggedIn;
|
use App\Events\UserLoggedIn;
|
||||||
|
use App\Events\UserSettingsChanged;
|
||||||
|
|
||||||
class AccountController extends BaseController
|
class AccountController extends BaseController
|
||||||
{
|
{
|
||||||
@ -341,8 +342,29 @@ class AccountController extends BaseController
|
|||||||
private function saveInvoiceSettings()
|
private function saveInvoiceSettings()
|
||||||
{
|
{
|
||||||
if (Auth::user()->account->isPro()) {
|
if (Auth::user()->account->isPro()) {
|
||||||
$account = Auth::user()->account;
|
|
||||||
|
|
||||||
|
$rules = [];
|
||||||
|
$user = Auth::user();
|
||||||
|
$iframeURL = preg_replace('/[^a-zA-Z0-9_\-\:\/\.]/', '', substr(strtolower(Input::get('iframe_url')), 0, MAX_IFRAME_URL_LENGTH));
|
||||||
|
$subdomain = preg_replace('/[^a-zA-Z0-9_\-\.]/', '', substr(strtolower(Input::get('substr(string, start)')), 0, MAX_SUBDOMAIN_LENGTH));
|
||||||
|
if (!$subdomain || in_array($subdomain, ['www', 'app', 'mail', 'admin', 'blog', 'user', 'contact', 'payment', 'payments', 'billing', 'invoice', 'business', 'owner'])) {
|
||||||
|
$subdomain = null;
|
||||||
|
}
|
||||||
|
if ($subdomain) {
|
||||||
|
$rules['subdomain'] = "unique:accounts,subdomain,{$user->account_id},id";
|
||||||
|
}
|
||||||
|
|
||||||
|
$validator = Validator::make(Input::all(), $rules);
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return Redirect::to('company/details')
|
||||||
|
->withErrors($validator)
|
||||||
|
->withInput();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$account = Auth::user()->account;
|
||||||
|
$account->subdomain = $subdomain;
|
||||||
|
$account->iframe_url = $iframeURL;
|
||||||
$account->custom_label1 = trim(Input::get('custom_label1'));
|
$account->custom_label1 = trim(Input::get('custom_label1'));
|
||||||
$account->custom_value1 = trim(Input::get('custom_value1'));
|
$account->custom_value1 = trim(Input::get('custom_value1'));
|
||||||
$account->custom_label2 = trim(Input::get('custom_label2'));
|
$account->custom_label2 = trim(Input::get('custom_label2'));
|
||||||
@ -375,6 +397,7 @@ class AccountController extends BaseController
|
|||||||
Session::flash('message', trans('texts.updated_settings'));
|
Session::flash('message', trans('texts.updated_settings'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Redirect::to('company/advanced_settings/invoice_settings');
|
return Redirect::to('company/advanced_settings/invoice_settings');
|
||||||
}
|
}
|
||||||
@ -643,14 +666,6 @@ class AccountController extends BaseController
|
|||||||
$rules['email'] = 'email|required|unique:users,email,'.$user->id.',id';
|
$rules['email'] = 'email|required|unique:users,email,'.$user->id.',id';
|
||||||
}
|
}
|
||||||
|
|
||||||
$subdomain = preg_replace('/[^a-zA-Z0-9_\-]/', '', substr(strtolower(Input::get('subdomain')), 0, MAX_SUBDOMAIN_LENGTH));
|
|
||||||
if (!$subdomain || in_array($subdomain, ['www', 'app', 'mail', 'admin', 'blog', 'user', 'contact', 'payment', 'payments', 'billing', 'invoice', 'business', 'owner'])) {
|
|
||||||
$subdomain = null;
|
|
||||||
}
|
|
||||||
if ($subdomain) {
|
|
||||||
$rules['subdomain'] = "unique:accounts,subdomain,{$user->account_id},id";
|
|
||||||
}
|
|
||||||
|
|
||||||
$validator = Validator::make(Input::all(), $rules);
|
$validator = Validator::make(Input::all(), $rules);
|
||||||
|
|
||||||
if ($validator->fails()) {
|
if ($validator->fails()) {
|
||||||
@ -660,7 +675,6 @@ class AccountController extends BaseController
|
|||||||
} else {
|
} else {
|
||||||
$account = Auth::user()->account;
|
$account = Auth::user()->account;
|
||||||
$account->name = trim(Input::get('name'));
|
$account->name = trim(Input::get('name'));
|
||||||
$account->subdomain = $subdomain;
|
|
||||||
$account->id_number = trim(Input::get('id_number'));
|
$account->id_number = trim(Input::get('id_number'));
|
||||||
$account->vat_number = trim(Input::get('vat_number'));
|
$account->vat_number = trim(Input::get('vat_number'));
|
||||||
$account->work_email = trim(Input::get('work_email'));
|
$account->work_email = trim(Input::get('work_email'));
|
||||||
@ -678,6 +692,7 @@ class AccountController extends BaseController
|
|||||||
$account->datetime_format_id = Input::get('datetime_format_id') ? Input::get('datetime_format_id') : null;
|
$account->datetime_format_id = Input::get('datetime_format_id') ? Input::get('datetime_format_id') : null;
|
||||||
$account->currency_id = Input::get('currency_id') ? Input::get('currency_id') : 1; // US Dollar
|
$account->currency_id = Input::get('currency_id') ? Input::get('currency_id') : 1; // US Dollar
|
||||||
$account->language_id = Input::get('language_id') ? Input::get('language_id') : 1; // English
|
$account->language_id = Input::get('language_id') ? Input::get('language_id') : 1; // English
|
||||||
|
$account->military_time = Input::get('military_time') ? true : false;
|
||||||
$account->save();
|
$account->save();
|
||||||
|
|
||||||
if (Auth::user()->id === $user->id) {
|
if (Auth::user()->id === $user->id) {
|
||||||
@ -718,8 +733,9 @@ class AccountController extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Session::flash('message', trans('texts.updated_settings'));
|
Event::fire(new UserSettingsChanged());
|
||||||
|
|
||||||
|
Session::flash('message', trans('texts.updated_settings'));
|
||||||
return Redirect::to('company/details');
|
return Redirect::to('company/details');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,10 +194,12 @@ class ClientController extends BaseController
|
|||||||
private static function getViewModel()
|
private static function getViewModel()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
'account' => Auth::user()->account,
|
||||||
'sizes' => Cache::get('sizes'),
|
'sizes' => Cache::get('sizes'),
|
||||||
'paymentTerms' => Cache::get('paymentTerms'),
|
'paymentTerms' => Cache::get('paymentTerms'),
|
||||||
'industries' => Cache::get('industries'),
|
'industries' => Cache::get('industries'),
|
||||||
'currencies' => Cache::get('currencies'),
|
'currencies' => Cache::get('currencies'),
|
||||||
|
'languages' => Cache::get('languages'),
|
||||||
'countries' => Cache::get('countries'),
|
'countries' => Cache::get('countries'),
|
||||||
'customLabel1' => Auth::user()->account->custom_client_label1,
|
'customLabel1' => Auth::user()->account->custom_client_label1,
|
||||||
'customLabel2' => Auth::user()->account->custom_client_label2,
|
'customLabel2' => Auth::user()->account->custom_client_label2,
|
||||||
@ -252,6 +254,7 @@ class ClientController extends BaseController
|
|||||||
$client->size_id = Input::get('size_id') ?: null;
|
$client->size_id = Input::get('size_id') ?: null;
|
||||||
$client->industry_id = Input::get('industry_id') ?: null;
|
$client->industry_id = Input::get('industry_id') ?: null;
|
||||||
$client->currency_id = Input::get('currency_id') ?: null;
|
$client->currency_id = Input::get('currency_id') ?: null;
|
||||||
|
$client->language_id = Input::get('language_id') ?: null;
|
||||||
$client->payment_terms = Input::get('payment_terms') ?: 0;
|
$client->payment_terms = Input::get('payment_terms') ?: 0;
|
||||||
$client->website = trim(Input::get('website'));
|
$client->website = trim(Input::get('website'));
|
||||||
|
|
||||||
|
@ -41,6 +41,11 @@ class HomeController extends BaseController
|
|||||||
return View::make('public.terms', ['hideHeader' => true]);
|
return View::make('public.terms', ['hideHeader' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function viewLogo()
|
||||||
|
{
|
||||||
|
return View::make('public.logo');
|
||||||
|
}
|
||||||
|
|
||||||
public function invoiceNow()
|
public function invoiceNow()
|
||||||
{
|
{
|
||||||
if (Auth::check() && Input::get('new_company')) {
|
if (Auth::check() && Input::get('new_company')) {
|
||||||
|
@ -108,7 +108,7 @@ class InvoiceApiController extends Controller
|
|||||||
if ($error) {
|
if ($error) {
|
||||||
$response = json_encode($error, JSON_PRETTY_PRINT);
|
$response = json_encode($error, JSON_PRETTY_PRINT);
|
||||||
} else {
|
} else {
|
||||||
$data = self::prepareData($data);
|
$data = self::prepareData($data, $client);
|
||||||
$data['client_id'] = $client->id;
|
$data['client_id'] = $client->id;
|
||||||
$invoice = $this->invoiceRepo->save(false, $data, false);
|
$invoice = $this->invoiceRepo->save(false, $data, false);
|
||||||
|
|
||||||
@ -136,10 +136,10 @@ class InvoiceApiController extends Controller
|
|||||||
return Response::make($response, $error ? 400 : 200, $headers);
|
return Response::make($response, $error ? 400 : 200, $headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function prepareData($data)
|
private function prepareData($data, $client)
|
||||||
{
|
{
|
||||||
$account = Auth::user()->account;
|
$account = Auth::user()->account;
|
||||||
$account->loadLocalizationSettings();
|
$account->loadLocalizationSettings($client);
|
||||||
|
|
||||||
// set defaults for optional fields
|
// set defaults for optional fields
|
||||||
$fields = [
|
$fields = [
|
||||||
|
@ -206,7 +206,7 @@ class InvoiceController extends BaseController
|
|||||||
Session::set($invitationKey, true);
|
Session::set($invitationKey, true);
|
||||||
Session::set('invitation_key', $invitationKey);
|
Session::set('invitation_key', $invitationKey);
|
||||||
|
|
||||||
$account->loadLocalizationSettings();
|
$account->loadLocalizationSettings($client);
|
||||||
|
|
||||||
$invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date);
|
$invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date);
|
||||||
$invoice->due_date = Utils::fromSqlDate($invoice->due_date);
|
$invoice->due_date = Utils::fromSqlDate($invoice->due_date);
|
||||||
@ -296,6 +296,7 @@ class InvoiceController extends BaseController
|
|||||||
$invoice->due_date = Utils::fromSqlDate($invoice->due_date);
|
$invoice->due_date = Utils::fromSqlDate($invoice->due_date);
|
||||||
$invoice->start_date = Utils::fromSqlDate($invoice->start_date);
|
$invoice->start_date = Utils::fromSqlDate($invoice->start_date);
|
||||||
$invoice->end_date = Utils::fromSqlDate($invoice->end_date);
|
$invoice->end_date = Utils::fromSqlDate($invoice->end_date);
|
||||||
|
$invoice->last_sent_date = Utils::fromSqlDate($invoice->last_sent_date);
|
||||||
$invoice->is_pro = Auth::user()->isPro();
|
$invoice->is_pro = Auth::user()->isPro();
|
||||||
|
|
||||||
$actions = [
|
$actions = [
|
||||||
@ -417,6 +418,7 @@ class InvoiceController extends BaseController
|
|||||||
'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(),
|
'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(),
|
||||||
'taxRates' => TaxRate::scope()->orderBy('name')->get(),
|
'taxRates' => TaxRate::scope()->orderBy('name')->get(),
|
||||||
'currencies' => Cache::get('currencies'),
|
'currencies' => Cache::get('currencies'),
|
||||||
|
'languages' => Cache::get('languages'),
|
||||||
'sizes' => Cache::get('sizes'),
|
'sizes' => Cache::get('sizes'),
|
||||||
'paymentTerms' => Cache::get('paymentTerms'),
|
'paymentTerms' => Cache::get('paymentTerms'),
|
||||||
'industries' => Cache::get('industries'),
|
'industries' => Cache::get('industries'),
|
||||||
@ -531,7 +533,12 @@ class InvoiceController extends BaseController
|
|||||||
if ($invoice->is_recurring) {
|
if ($invoice->is_recurring) {
|
||||||
if ($invoice->shouldSendToday()) {
|
if ($invoice->shouldSendToday()) {
|
||||||
$invoice = $this->invoiceRepo->createRecurringInvoice($invoice);
|
$invoice = $this->invoiceRepo->createRecurringInvoice($invoice);
|
||||||
|
// in case auto-bill is enabled
|
||||||
|
if ($invoice->isPaid()) {
|
||||||
|
$response = true;
|
||||||
|
} else {
|
||||||
$response = $this->mailer->sendInvoice($invoice);
|
$response = $this->mailer->sendInvoice($invoice);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$response = trans('texts.recurring_too_soon');
|
$response = trans('texts.recurring_too_soon');
|
||||||
}
|
}
|
||||||
|
@ -12,29 +12,22 @@ use Omnipay;
|
|||||||
use CreditCard;
|
use CreditCard;
|
||||||
use URL;
|
use URL;
|
||||||
use Cache;
|
use Cache;
|
||||||
use Event;
|
|
||||||
use DateTime;
|
|
||||||
use App\Models\Account;
|
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\Invitation;
|
use App\Models\Invitation;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\PaymentType;
|
use App\Models\PaymentType;
|
||||||
use App\Models\Country;
|
|
||||||
use App\Models\License;
|
use App\Models\License;
|
||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use App\Models\Affiliate;
|
use App\Models\Affiliate;
|
||||||
use App\Models\AccountGatewayToken;
|
|
||||||
use App\Ninja\Repositories\PaymentRepository;
|
use App\Ninja\Repositories\PaymentRepository;
|
||||||
use App\Ninja\Repositories\InvoiceRepository;
|
use App\Ninja\Repositories\InvoiceRepository;
|
||||||
use App\Ninja\Repositories\AccountRepository;
|
use App\Ninja\Repositories\AccountRepository;
|
||||||
use App\Ninja\Mailers\ContactMailer;
|
use App\Ninja\Mailers\ContactMailer;
|
||||||
use App\Events\InvoicePaid;
|
use App\Services\PaymentService;
|
||||||
|
|
||||||
class PaymentController extends BaseController
|
class PaymentController extends BaseController
|
||||||
{
|
{
|
||||||
protected $creditRepo;
|
public function __construct(PaymentRepository $paymentRepo, InvoiceRepository $invoiceRepo, AccountRepository $accountRepo, ContactMailer $contactMailer, PaymentService $paymentService)
|
||||||
|
|
||||||
public function __construct(PaymentRepository $paymentRepo, InvoiceRepository $invoiceRepo, AccountRepository $accountRepo, ContactMailer $contactMailer)
|
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
|
||||||
@ -42,6 +35,7 @@ class PaymentController extends BaseController
|
|||||||
$this->invoiceRepo = $invoiceRepo;
|
$this->invoiceRepo = $invoiceRepo;
|
||||||
$this->accountRepo = $accountRepo;
|
$this->accountRepo = $accountRepo;
|
||||||
$this->contactMailer = $contactMailer;
|
$this->contactMailer = $contactMailer;
|
||||||
|
$this->paymentService = $paymentService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
@ -191,36 +185,9 @@ class PaymentController extends BaseController
|
|||||||
return View::make('payments.edit', $data);
|
return View::make('payments.edit', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createGateway($accountGateway)
|
|
||||||
{
|
|
||||||
$gateway = Omnipay::create($accountGateway->gateway->provider);
|
|
||||||
$config = json_decode($accountGateway->config);
|
|
||||||
|
|
||||||
foreach ($config as $key => $val) {
|
|
||||||
if (!$val) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$function = "set".ucfirst($key);
|
|
||||||
$gateway->$function($val);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($accountGateway->gateway->id == GATEWAY_DWOLLA) {
|
|
||||||
if ($gateway->getSandbox() && isset($_ENV['DWOLLA_SANDBOX_KEY']) && isset($_ENV['DWOLLA_SANSBOX_SECRET'])) {
|
|
||||||
$gateway->setKey($_ENV['DWOLLA_SANDBOX_KEY']);
|
|
||||||
$gateway->setSecret($_ENV['DWOLLA_SANSBOX_SECRET']);
|
|
||||||
} elseif (isset($_ENV['DWOLLA_KEY']) && isset($_ENV['DWOLLA_SECRET'])) {
|
|
||||||
$gateway->setKey($_ENV['DWOLLA_KEY']);
|
|
||||||
$gateway->setSecret($_ENV['DWOLLA_SECRET']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $gateway;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getLicensePaymentDetails($input, $affiliate)
|
private function getLicensePaymentDetails($input, $affiliate)
|
||||||
{
|
{
|
||||||
$data = self::convertInputForOmnipay($input);
|
$data = $this->paymentService->convertInputForOmnipay($input);
|
||||||
$card = new CreditCard($data);
|
$card = new CreditCard($data);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@ -232,67 +199,6 @@ class PaymentController extends BaseController
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function convertInputForOmnipay($input)
|
|
||||||
{
|
|
||||||
$data = [
|
|
||||||
'firstName' => $input['first_name'],
|
|
||||||
'lastName' => $input['last_name'],
|
|
||||||
'number' => $input['card_number'],
|
|
||||||
'expiryMonth' => $input['expiration_month'],
|
|
||||||
'expiryYear' => $input['expiration_year'],
|
|
||||||
'cvv' => $input['cvv'],
|
|
||||||
];
|
|
||||||
|
|
||||||
if (isset($input['country_id'])) {
|
|
||||||
$country = Country::find($input['country_id']);
|
|
||||||
|
|
||||||
$data = array_merge($data, [
|
|
||||||
'billingAddress1' => $input['address1'],
|
|
||||||
'billingAddress2' => $input['address2'],
|
|
||||||
'billingCity' => $input['city'],
|
|
||||||
'billingState' => $input['state'],
|
|
||||||
'billingPostcode' => $input['postal_code'],
|
|
||||||
'billingCountry' => $country->iso_3166_2,
|
|
||||||
'shippingAddress1' => $input['address1'],
|
|
||||||
'shippingAddress2' => $input['address2'],
|
|
||||||
'shippingCity' => $input['city'],
|
|
||||||
'shippingState' => $input['state'],
|
|
||||||
'shippingPostcode' => $input['postal_code'],
|
|
||||||
'shippingCountry' => $country->iso_3166_2
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getPaymentDetails($invitation, $input = null)
|
|
||||||
{
|
|
||||||
$invoice = $invitation->invoice;
|
|
||||||
$account = $invoice->account;
|
|
||||||
$key = $invoice->account_id.'-'.$invoice->invoice_number;
|
|
||||||
$currencyCode = $invoice->client->currency ? $invoice->client->currency->code : ($invoice->account->currency ? $invoice->account->currency->code : 'USD');
|
|
||||||
|
|
||||||
if ($input) {
|
|
||||||
$data = self::convertInputForOmnipay($input);
|
|
||||||
Session::put($key, $data);
|
|
||||||
} elseif (Session::get($key)) {
|
|
||||||
$data = Session::get($key);
|
|
||||||
} else {
|
|
||||||
$data = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$card = new CreditCard($data);
|
|
||||||
|
|
||||||
return [
|
|
||||||
'amount' => $invoice->getRequestedAmount(),
|
|
||||||
'card' => $card,
|
|
||||||
'currency' => $currencyCode,
|
|
||||||
'returnUrl' => URL::to('complete'),
|
|
||||||
'cancelUrl' => $invitation->getLink(),
|
|
||||||
'description' => trans('texts.' . $invoice->getEntityType()) . " {$invoice->invoice_number}",
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function show_payment($invitationKey, $paymentType = false)
|
public function show_payment($invitationKey, $paymentType = false)
|
||||||
{
|
{
|
||||||
$invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.account_gateways.gateway')->where('invitation_key', '=', $invitationKey)->firstOrFail();
|
$invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.account_gateways.gateway')->where('invitation_key', '=', $invitationKey)->firstOrFail();
|
||||||
@ -434,21 +340,13 @@ class PaymentController extends BaseController
|
|||||||
if ($testMode) {
|
if ($testMode) {
|
||||||
$ref = 'TEST_MODE';
|
$ref = 'TEST_MODE';
|
||||||
} else {
|
} else {
|
||||||
$gateway = self::createGateway($accountGateway);
|
$gateway = $this->paymentService->createGateway($accountGateway);
|
||||||
$details = self::getLicensePaymentDetails(Input::all(), $affiliate);
|
$details = self::getLicensePaymentDetails(Input::all(), $affiliate);
|
||||||
$response = $gateway->purchase($details)->send();
|
$response = $gateway->purchase($details)->send();
|
||||||
$ref = $response->getTransactionReference();
|
$ref = $response->getTransactionReference();
|
||||||
|
|
||||||
if (!$ref) {
|
if (!$response->isSuccessful() || !$ref) {
|
||||||
Session::flash('error', $response->getMessage());
|
$this->error('License', $response->getMessage(), $accountGateway);
|
||||||
|
|
||||||
return Redirect::to('license')->withInput();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$response->isSuccessful()) {
|
|
||||||
Session::flash('error', $response->getMessage());
|
|
||||||
Utils::logError('Payment Error [license]: ' . $response->getMessage());
|
|
||||||
|
|
||||||
return Redirect::to('license')->withInput();
|
return Redirect::to('license')->withInput();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -482,10 +380,7 @@ class PaymentController extends BaseController
|
|||||||
|
|
||||||
return View::make('public.license', $data);
|
return View::make('public.license', $data);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$errorMessage = trans('texts.payment_error');
|
$this->error('License-Uncaught', false, $accountGateway, $e);
|
||||||
Session::flash('error', $errorMessage);
|
|
||||||
Utils::logError('Payment Error [license-uncaught]: ' . Utils::getErrorString($e));
|
|
||||||
|
|
||||||
return Redirect::to('license')->withInput();
|
return Redirect::to('license')->withInput();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -549,7 +444,6 @@ class PaymentController extends BaseController
|
|||||||
->withInput();
|
->withInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ($accountGateway->update_address) {
|
if ($accountGateway->update_address) {
|
||||||
$client->address1 = trim(Input::get('address1'));
|
$client->address1 = trim(Input::get('address1'));
|
||||||
$client->address2 = trim(Input::get('address2'));
|
$client->address2 = trim(Input::get('address2'));
|
||||||
@ -562,35 +456,19 @@ class PaymentController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$gateway = self::createGateway($accountGateway);
|
$gateway = $this->paymentService->createGateway($accountGateway);
|
||||||
$details = self::getPaymentDetails($invitation, ($useToken || !$onSite) ? false : Input::all());
|
$details = $this->paymentService->getPaymentDetails($invitation, ($useToken || !$onSite) ? false : Input::all());
|
||||||
|
|
||||||
|
// check if we're creating/using a billing token
|
||||||
if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
|
if ($accountGateway->gateway_id == GATEWAY_STRIPE) {
|
||||||
if ($useToken) {
|
if ($useToken) {
|
||||||
$details['cardReference'] = $client->getGatewayToken();
|
$details['cardReference'] = $client->getGatewayToken();
|
||||||
} elseif ($account->token_billing_type_id == TOKEN_BILLING_ALWAYS || Input::get('token_billing')) {
|
} elseif ($account->token_billing_type_id == TOKEN_BILLING_ALWAYS || Input::get('token_billing')) {
|
||||||
$tokenResponse = $gateway->createCard($details)->send();
|
$token = $this->paymentService->createToken($gateway, $details, $accountGateway, $client, $invitation->contact_id);
|
||||||
$cardReference = $tokenResponse->getCardReference();
|
if ($token) {
|
||||||
|
$details['cardReference'] = $token;
|
||||||
if ($cardReference) {
|
|
||||||
$details['cardReference'] = $cardReference;
|
|
||||||
|
|
||||||
$token = AccountGatewayToken::where('client_id', '=', $client->id)
|
|
||||||
->where('account_gateway_id', '=', $accountGateway->id)->first();
|
|
||||||
|
|
||||||
if (!$token) {
|
|
||||||
$token = new AccountGatewayToken();
|
|
||||||
$token->account_id = $account->id;
|
|
||||||
$token->contact_id = $invitation->contact_id;
|
|
||||||
$token->account_gateway_id = $accountGateway->id;
|
|
||||||
$token->client_id = $client->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
$token->token = $cardReference;
|
|
||||||
$token->save();
|
|
||||||
} else {
|
} else {
|
||||||
Session::flash('error', $tokenResponse->getMessage());
|
$this->error('Token-No-Ref', $this->paymentService->lastError, $accountGateway);
|
||||||
Utils::logError('Payment Error [no-token-ref]: ' . $tokenResponse->getMessage());
|
|
||||||
return Redirect::to('payment/'.$invitationKey)->withInput();
|
return Redirect::to('payment/'.$invitationKey)->withInput();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -600,9 +478,7 @@ class PaymentController extends BaseController
|
|||||||
$ref = $response->getTransactionReference();
|
$ref = $response->getTransactionReference();
|
||||||
|
|
||||||
if (!$ref) {
|
if (!$ref) {
|
||||||
|
$this->error('No-Ref', $response->getMessage(), $accountGateway);
|
||||||
Session::flash('error', $response->getMessage());
|
|
||||||
Utils::logError('Payment Error [no-ref]: ' . $response->getMessage());
|
|
||||||
|
|
||||||
if ($onSite) {
|
if ($onSite) {
|
||||||
return Redirect::to('payment/'.$invitationKey)->withInput();
|
return Redirect::to('payment/'.$invitationKey)->withInput();
|
||||||
@ -612,7 +488,7 @@ class PaymentController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($response->isSuccessful()) {
|
if ($response->isSuccessful()) {
|
||||||
$payment = self::createPayment($invitation, $ref);
|
$payment = $this->paymentService->createPayment($invitation, $ref);
|
||||||
Session::flash('message', trans('texts.applied_payment'));
|
Session::flash('message', trans('texts.applied_payment'));
|
||||||
|
|
||||||
if ($account->account_key == NINJA_ACCOUNT_KEY) {
|
if ($account->account_key == NINJA_ACCOUNT_KEY) {
|
||||||
@ -629,16 +505,11 @@ class PaymentController extends BaseController
|
|||||||
Session::save();
|
Session::save();
|
||||||
$response->redirect();
|
$response->redirect();
|
||||||
} else {
|
} else {
|
||||||
Session::flash('error', $response->getMessage());
|
$this->error('Fatal', $response->getMessage(), $accountGateway);
|
||||||
Utils::logError('Payment Error [fatal]: ' . $response->getMessage());
|
|
||||||
|
|
||||||
return Utils::fatalError('Sorry, there was an error processing your payment. Please try again later.<p>', $response->getMessage());
|
return Utils::fatalError('Sorry, there was an error processing your payment. Please try again later.<p>', $response->getMessage());
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$errorMessage = trans('texts.payment_error');
|
$this->error('Uncaught', false, $accountGateway, $e);
|
||||||
Session::flash('error', $errorMessage."<p>".$e->getMessage());
|
|
||||||
Utils::logError('Payment Error [uncaught]:' . Utils::getErrorString($e));
|
|
||||||
|
|
||||||
if ($onSite) {
|
if ($onSite) {
|
||||||
return Redirect::to('payment/'.$invitationKey)->withInput();
|
return Redirect::to('payment/'.$invitationKey)->withInput();
|
||||||
} else {
|
} else {
|
||||||
@ -647,47 +518,6 @@ class PaymentController extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createPayment($invitation, $ref, $payerId = null)
|
|
||||||
{
|
|
||||||
$invoice = $invitation->invoice;
|
|
||||||
$accountGateway = $invoice->client->account->getGatewayByType(Session::get('payment_type'));
|
|
||||||
|
|
||||||
if ($invoice->account->account_key == NINJA_ACCOUNT_KEY
|
|
||||||
&& $invoice->amount == PRO_PLAN_PRICE) {
|
|
||||||
$account = Account::with('users')->find($invoice->client->public_id);
|
|
||||||
if ($account->pro_plan_paid && $account->pro_plan_paid != '0000-00-00') {
|
|
||||||
$date = DateTime::createFromFormat('Y-m-d', $account->pro_plan_paid);
|
|
||||||
$account->pro_plan_paid = $date->modify('+1 year')->format('Y-m-d');
|
|
||||||
} else {
|
|
||||||
$account->pro_plan_paid = date_create()->format('Y-m-d');
|
|
||||||
}
|
|
||||||
$account->save();
|
|
||||||
|
|
||||||
$user = $account->users()->first();
|
|
||||||
$this->accountRepo->syncAccounts($user->id, $account->pro_plan_paid);
|
|
||||||
}
|
|
||||||
|
|
||||||
$payment = Payment::createNew($invitation);
|
|
||||||
$payment->invitation_id = $invitation->id;
|
|
||||||
$payment->account_gateway_id = $accountGateway->id;
|
|
||||||
$payment->invoice_id = $invoice->id;
|
|
||||||
$payment->amount = $invoice->getRequestedAmount();
|
|
||||||
$payment->client_id = $invoice->client_id;
|
|
||||||
$payment->contact_id = $invitation->contact_id;
|
|
||||||
$payment->transaction_reference = $ref;
|
|
||||||
$payment->payment_date = date_create()->format('Y-m-d');
|
|
||||||
|
|
||||||
if ($payerId) {
|
|
||||||
$payment->payer_id = $payerId;
|
|
||||||
}
|
|
||||||
|
|
||||||
$payment->save();
|
|
||||||
|
|
||||||
Event::fire(new InvoicePaid($payment));
|
|
||||||
|
|
||||||
return $payment;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function offsite_payment()
|
public function offsite_payment()
|
||||||
{
|
{
|
||||||
$payerId = Request::query('PayerID');
|
$payerId = Request::query('PayerID');
|
||||||
@ -705,45 +535,37 @@ class PaymentController extends BaseController
|
|||||||
$invoice = $invitation->invoice;
|
$invoice = $invitation->invoice;
|
||||||
|
|
||||||
$accountGateway = $invoice->client->account->getGatewayByType(Session::get('payment_type'));
|
$accountGateway = $invoice->client->account->getGatewayByType(Session::get('payment_type'));
|
||||||
$gateway = self::createGateway($accountGateway);
|
$gateway = $this->paymentService->createGateway($accountGateway);
|
||||||
|
|
||||||
// Check for Dwolla payment error
|
// Check for Dwolla payment error
|
||||||
if ($accountGateway->isGateway(GATEWAY_DWOLLA) && Input::get('error')) {
|
if ($accountGateway->isGateway(GATEWAY_DWOLLA) && Input::get('error')) {
|
||||||
$errorMessage = trans('texts.payment_error')."\n\n".Input::get('error_description');
|
$this->error('Dwolla', Input::get('error_description'), $accountGateway);
|
||||||
Session::flash('error', $errorMessage);
|
|
||||||
Utils::logError('Payment Error [dwolla]: ' . $errorMessage);
|
|
||||||
return Redirect::to('view/'.$invitation->invitation_key);
|
return Redirect::to('view/'.$invitation->invitation_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (method_exists($gateway, 'completePurchase')) {
|
if (method_exists($gateway, 'completePurchase')) {
|
||||||
$details = self::getPaymentDetails($invitation);
|
$details = $this->paymentService->getPaymentDetails($invitation);
|
||||||
$response = $gateway->completePurchase($details)->send();
|
$response = $gateway->completePurchase($details)->send();
|
||||||
$ref = $response->getTransactionReference();
|
$ref = $response->getTransactionReference();
|
||||||
|
|
||||||
if ($response->isSuccessful()) {
|
if ($response->isSuccessful()) {
|
||||||
$payment = self::createPayment($invitation, $ref, $payerId);
|
$payment = $this->paymentService->createPayment($invitation, $ref, $payerId);
|
||||||
Session::flash('message', trans('texts.applied_payment'));
|
Session::flash('message', trans('texts.applied_payment'));
|
||||||
|
|
||||||
return Redirect::to('view/'.$invitation->invitation_key);
|
return Redirect::to('view/'.$invitation->invitation_key);
|
||||||
} else {
|
} else {
|
||||||
$errorMessage = trans('texts.payment_error')."\n\n".$response->getMessage();
|
$this->error('offsite', $response->getMessage(), $accountGateway);
|
||||||
Session::flash('error', $errorMessage);
|
|
||||||
Utils::logError('Payment Error [offsite]: ' . $errorMessage);
|
|
||||||
|
|
||||||
return Redirect::to('view/'.$invitation->invitation_key);
|
return Redirect::to('view/'.$invitation->invitation_key);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$payment = self::createPayment($invitation, $token, $payerId);
|
$payment = $this->paymentService->createPayment($invitation, $token, $payerId);
|
||||||
Session::flash('message', trans('texts.applied_payment'));
|
Session::flash('message', trans('texts.applied_payment'));
|
||||||
|
|
||||||
return Redirect::to('view/'.$invitation->invitation_key);
|
return Redirect::to('view/'.$invitation->invitation_key);
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$errorMessage = trans('texts.payment_error');
|
$this->error('Offsite-uncaught', false, $accountGateway, $e);
|
||||||
Session::flash('error', $errorMessage);
|
|
||||||
Utils::logError('Payment Error [offsite-uncaught]: ' . $errorMessage."\n\n".$e->getMessage());
|
|
||||||
|
|
||||||
return Redirect::to('view/'.$invitation->invitation_key);
|
return Redirect::to('view/'.$invitation->invitation_key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -799,4 +621,24 @@ class PaymentController extends BaseController
|
|||||||
|
|
||||||
return Redirect::to('payments');
|
return Redirect::to('payments');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function error($type, $error, $accountGateway, $exception = false)
|
||||||
|
{
|
||||||
|
if (!$error) {
|
||||||
|
if ($exception) {
|
||||||
|
$error = $exception->getMessage();
|
||||||
|
} else {
|
||||||
|
$error = trans('texts.payment_error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$message = '';
|
||||||
|
if ($accountGateway && $accountGateway->gateway) {
|
||||||
|
$message = $accountGateway->gateway->name . ': ';
|
||||||
|
}
|
||||||
|
$message .= $error;
|
||||||
|
|
||||||
|
Session::flash('error', $message);
|
||||||
|
Utils::logError("Payment Error [{$type}]: " . ($exception ? Utils::getErrorString($exception) : $message));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,6 +156,7 @@ class QuoteController extends BaseController
|
|||||||
'currencies' => Cache::get('currencies'),
|
'currencies' => Cache::get('currencies'),
|
||||||
'sizes' => Cache::get('sizes'),
|
'sizes' => Cache::get('sizes'),
|
||||||
'paymentTerms' => Cache::get('paymentTerms'),
|
'paymentTerms' => Cache::get('paymentTerms'),
|
||||||
|
'languages' => Cache::get('languages'),
|
||||||
'industries' => Cache::get('industries'),
|
'industries' => Cache::get('industries'),
|
||||||
'invoiceDesigns' => InvoiceDesign::getDesigns(),
|
'invoiceDesigns' => InvoiceDesign::getDesigns(),
|
||||||
'invoiceLabels' => Auth::user()->account->getInvoiceLabels(),
|
'invoiceLabels' => Auth::user()->account->getInvoiceLabels(),
|
||||||
|
@ -133,7 +133,7 @@ class TaskController extends BaseController
|
|||||||
'url' => 'tasks',
|
'url' => 'tasks',
|
||||||
'title' => trans('texts.new_task'),
|
'title' => trans('texts.new_task'),
|
||||||
'timezone' => Auth::user()->account->timezone ? Auth::user()->account->timezone->name : DEFAULT_TIMEZONE,
|
'timezone' => Auth::user()->account->timezone ? Auth::user()->account->timezone->name : DEFAULT_TIMEZONE,
|
||||||
'datetimeFormat' => Auth::user()->account->datetime_format ? Auth::user()->account->datetime_format->format_moment : DEFAULT_DATETIME_MOMENT_FORMAT
|
'datetimeFormat' => Auth::user()->account->getMomentDateTimeFormat(),
|
||||||
];
|
];
|
||||||
|
|
||||||
$data = array_merge($data, self::getViewModel());
|
$data = array_merge($data, self::getViewModel());
|
||||||
@ -182,7 +182,7 @@ class TaskController extends BaseController
|
|||||||
'duration' => $task->is_running ? $task->getCurrentDuration() : $task->getDuration(),
|
'duration' => $task->is_running ? $task->getCurrentDuration() : $task->getDuration(),
|
||||||
'actions' => $actions,
|
'actions' => $actions,
|
||||||
'timezone' => Auth::user()->account->timezone ? Auth::user()->account->timezone->name : DEFAULT_TIMEZONE,
|
'timezone' => Auth::user()->account->timezone ? Auth::user()->account->timezone->name : DEFAULT_TIMEZONE,
|
||||||
'datetimeFormat' => Auth::user()->account->datetime_format ? Auth::user()->account->datetime_format->format_moment : DEFAULT_DATETIME_MOMENT_FORMAT
|
'datetimeFormat' => Auth::user()->account->getMomentDateTimeFormat(),
|
||||||
];
|
];
|
||||||
|
|
||||||
$data = array_merge($data, self::getViewModel());
|
$data = array_merge($data, self::getViewModel());
|
||||||
|
@ -384,4 +384,29 @@ class UserController extends BaseController
|
|||||||
{
|
{
|
||||||
return View::make('users.account_management');
|
return View::make('users.account_management');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function claimReferralCode($email)
|
||||||
|
{
|
||||||
|
$user = User::whereEmail($email)
|
||||||
|
->whereReferralCode(null)
|
||||||
|
->whereConfirmed(true)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($user) {
|
||||||
|
do {
|
||||||
|
$code = strtoupper(str_random(8));
|
||||||
|
$match = User::whereReferralCode($code)
|
||||||
|
->withTrashed()
|
||||||
|
->first();
|
||||||
|
} while ($match);
|
||||||
|
|
||||||
|
$user->referral_code = $code;
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
return $code;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Redirect::to('/');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -118,10 +118,15 @@ class StartupCheck
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif (Auth::check()) {
|
} elseif (Auth::check()) {
|
||||||
$locale = Session::get(SESSION_LOCALE, DEFAULT_LOCALE);
|
$locale = Auth::user()->account->language ? Auth::user()->account->language->locale : DEFAULT_LOCALE;
|
||||||
App::setLocale($locale);
|
App::setLocale($locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track the referral code
|
||||||
|
if (Input::has('rc')) {
|
||||||
|
Session::set(SESSION_REFERRAL_CODE, Input::get('rc'));
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure the account/user localization settings are in the session
|
// Make sure the account/user localization settings are in the session
|
||||||
if (Auth::check() && !Session::has(SESSION_TIMEZONE)) {
|
if (Auth::check() && !Session::has(SESSION_TIMEZONE)) {
|
||||||
Event::fire(new UserSettingsChanged());
|
Event::fire(new UserSettingsChanged());
|
||||||
@ -164,9 +169,8 @@ class StartupCheck
|
|||||||
Session::flash('error', trans('texts.old_browser'));
|
Session::flash('error', trans('texts.old_browser'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// for security prevent displaying within an iframe
|
|
||||||
$response = $next($request);
|
$response = $next($request);
|
||||||
$response->headers->set('X-Frame-Options', 'DENY');
|
//$response->headers->set('X-Frame-Options', 'DENY');
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
@ -39,10 +39,12 @@ Route::get('terms', 'HomeController@showTerms');
|
|||||||
Route::get('log_error', 'HomeController@logError');
|
Route::get('log_error', 'HomeController@logError');
|
||||||
Route::get('invoice_now', 'HomeController@invoiceNow');
|
Route::get('invoice_now', 'HomeController@invoiceNow');
|
||||||
Route::get('keep_alive', 'HomeController@keepAlive');
|
Route::get('keep_alive', 'HomeController@keepAlive');
|
||||||
|
Route::get('referral_code/{email}', 'UserController@claimReferralCode');
|
||||||
Route::post('get_started', 'AccountController@getStarted');
|
Route::post('get_started', 'AccountController@getStarted');
|
||||||
|
|
||||||
// Client visible pages
|
// Client visible pages
|
||||||
Route::get('view/{invitation_key}', 'InvoiceController@view');
|
Route::get('view/{invitation_key}', 'InvoiceController@view');
|
||||||
|
Route::get('view', 'HomeController@viewLogo');
|
||||||
Route::get('approve/{invitation_key}', 'QuoteController@approve');
|
Route::get('approve/{invitation_key}', 'QuoteController@approve');
|
||||||
Route::get('payment/{invitation_key}/{payment_type?}', 'PaymentController@show_payment');
|
Route::get('payment/{invitation_key}/{payment_type?}', 'PaymentController@show_payment');
|
||||||
Route::post('payment/{invitation_key}', 'PaymentController@do_payment');
|
Route::post('payment/{invitation_key}', 'PaymentController@do_payment');
|
||||||
@ -306,6 +308,7 @@ if (!defined('CONTACT_EMAIL')) {
|
|||||||
define('MAX_NUM_CLIENTS_PRO', 20000);
|
define('MAX_NUM_CLIENTS_PRO', 20000);
|
||||||
define('MAX_NUM_USERS', 20);
|
define('MAX_NUM_USERS', 20);
|
||||||
define('MAX_SUBDOMAIN_LENGTH', 30);
|
define('MAX_SUBDOMAIN_LENGTH', 30);
|
||||||
|
define('MAX_IFRAME_URL_LENGTH', 250);
|
||||||
define('DEFAULT_FONT_SIZE', 9);
|
define('DEFAULT_FONT_SIZE', 9);
|
||||||
|
|
||||||
define('INVOICE_STATUS_DRAFT', 1);
|
define('INVOICE_STATUS_DRAFT', 1);
|
||||||
@ -333,6 +336,7 @@ if (!defined('CONTACT_EMAIL')) {
|
|||||||
define('SESSION_COUNTER', 'sessionCounter');
|
define('SESSION_COUNTER', 'sessionCounter');
|
||||||
define('SESSION_LOCALE', 'sessionLocale');
|
define('SESSION_LOCALE', 'sessionLocale');
|
||||||
define('SESSION_USER_ACCOUNTS', 'userAccounts');
|
define('SESSION_USER_ACCOUNTS', 'userAccounts');
|
||||||
|
define('SESSION_REFERRAL_CODE', 'referralCode');
|
||||||
|
|
||||||
define('SESSION_LAST_REQUEST_PAGE', 'SESSION_LAST_REQUEST_PAGE');
|
define('SESSION_LAST_REQUEST_PAGE', 'SESSION_LAST_REQUEST_PAGE');
|
||||||
define('SESSION_LAST_REQUEST_TIME', 'SESSION_LAST_REQUEST_TIME');
|
define('SESSION_LAST_REQUEST_TIME', 'SESSION_LAST_REQUEST_TIME');
|
||||||
@ -376,7 +380,7 @@ if (!defined('CONTACT_EMAIL')) {
|
|||||||
define('PREV_USER_ID', 'PREV_USER_ID');
|
define('PREV_USER_ID', 'PREV_USER_ID');
|
||||||
define('NINJA_ACCOUNT_KEY', 'zg4ylmzDkdkPOT8yoKQw9LTWaoZJx79h');
|
define('NINJA_ACCOUNT_KEY', 'zg4ylmzDkdkPOT8yoKQw9LTWaoZJx79h');
|
||||||
define('NINJA_GATEWAY_ID', GATEWAY_STRIPE);
|
define('NINJA_GATEWAY_ID', GATEWAY_STRIPE);
|
||||||
define('NINJA_GATEWAY_CONFIG', '');
|
define('NINJA_GATEWAY_CONFIG', 'NINJA_GATEWAY_CONFIG');
|
||||||
define('NINJA_WEB_URL', 'https://www.invoiceninja.com');
|
define('NINJA_WEB_URL', 'https://www.invoiceninja.com');
|
||||||
define('NINJA_APP_URL', 'https://app.invoiceninja.com');
|
define('NINJA_APP_URL', 'https://app.invoiceninja.com');
|
||||||
define('NINJA_VERSION', '2.3.4');
|
define('NINJA_VERSION', '2.3.4');
|
||||||
|
@ -341,10 +341,7 @@ class Utils
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//$timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE);
|
|
||||||
$format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT);
|
$format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT);
|
||||||
|
|
||||||
//$dateTime = DateTime::createFromFormat($format, $date, new DateTimeZone($timezone));
|
|
||||||
$dateTime = DateTime::createFromFormat($format, $date);
|
$dateTime = DateTime::createFromFormat($format, $date);
|
||||||
|
|
||||||
return $formatResult ? $dateTime->format('Y-m-d') : $dateTime;
|
return $formatResult ? $dateTime->format('Y-m-d') : $dateTime;
|
||||||
@ -356,11 +353,8 @@ class Utils
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
//$timezone = Session::get(SESSION_TIMEZONE, DEFAULT_TIMEZONE);
|
|
||||||
$format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT);
|
$format = Session::get(SESSION_DATE_FORMAT, DEFAULT_DATE_FORMAT);
|
||||||
|
|
||||||
$dateTime = DateTime::createFromFormat('Y-m-d', $date);
|
$dateTime = DateTime::createFromFormat('Y-m-d', $date);
|
||||||
//$dateTime->setTimeZone(new DateTimeZone($timezone));
|
|
||||||
|
|
||||||
return $formatResult ? $dateTime->format($format) : $dateTime;
|
return $formatResult ? $dateTime->format($format) : $dateTime;
|
||||||
}
|
}
|
||||||
@ -752,4 +746,34 @@ class Utils
|
|||||||
}
|
}
|
||||||
return $str;
|
return $str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getSubdomainPlaceholder() {
|
||||||
|
$parts = parse_url(SITE_URL);
|
||||||
|
$subdomain = '';
|
||||||
|
if (isset($parts['host'])) {
|
||||||
|
$host = explode('.', $parts['host']);
|
||||||
|
if (count($host) > 2) {
|
||||||
|
$subdomain = $host[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $subdomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getDomainPlaceholder() {
|
||||||
|
$parts = parse_url(SITE_URL);
|
||||||
|
$domain = '';
|
||||||
|
if (isset($parts['host'])) {
|
||||||
|
$host = explode('.', $parts['host']);
|
||||||
|
if (count($host) > 2) {
|
||||||
|
array_shift($host);
|
||||||
|
$domain .= implode('.', $host);
|
||||||
|
} else {
|
||||||
|
$domain .= $parts['host'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isset($parts['path'])) {
|
||||||
|
$domain .= $parts['path'];
|
||||||
|
}
|
||||||
|
return $domain;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,17 @@ class Account extends Eloquent
|
|||||||
return $user->getDisplayName();
|
return $user->getDisplayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMomentDateTimeFormat()
|
||||||
|
{
|
||||||
|
$format = $this->datetime_format ? $this->datetime_format->format_moment : DEFAULT_DATETIME_MOMENT_FORMAT;
|
||||||
|
|
||||||
|
if ($this->military_time) {
|
||||||
|
$format = str_replace('h:mm:ss a', 'H:mm:ss', $format);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $format;
|
||||||
|
}
|
||||||
|
|
||||||
public function getTimezone()
|
public function getTimezone()
|
||||||
{
|
{
|
||||||
if ($this->timezone) {
|
if ($this->timezone) {
|
||||||
@ -228,18 +239,27 @@ class Account extends Eloquent
|
|||||||
return $language->locale;
|
return $language->locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadLocalizationSettings()
|
public function loadLocalizationSettings($client = false)
|
||||||
{
|
{
|
||||||
$this->load('timezone', 'date_format', 'datetime_format', 'language');
|
$this->load('timezone', 'date_format', 'datetime_format', 'language');
|
||||||
|
|
||||||
Session::put(SESSION_TIMEZONE, $this->timezone ? $this->timezone->name : DEFAULT_TIMEZONE);
|
Session::put(SESSION_TIMEZONE, $this->timezone ? $this->timezone->name : DEFAULT_TIMEZONE);
|
||||||
Session::put(SESSION_DATE_FORMAT, $this->date_format ? $this->date_format->format : DEFAULT_DATE_FORMAT);
|
Session::put(SESSION_DATE_FORMAT, $this->date_format ? $this->date_format->format : DEFAULT_DATE_FORMAT);
|
||||||
Session::put(SESSION_DATE_PICKER_FORMAT, $this->date_format ? $this->date_format->picker_format : DEFAULT_DATE_PICKER_FORMAT);
|
Session::put(SESSION_DATE_PICKER_FORMAT, $this->date_format ? $this->date_format->picker_format : DEFAULT_DATE_PICKER_FORMAT);
|
||||||
Session::put(SESSION_DATETIME_FORMAT, $this->datetime_format ? $this->datetime_format->format : DEFAULT_DATETIME_FORMAT);
|
|
||||||
Session::put(SESSION_CURRENCY, $this->currency_id ? $this->currency_id : DEFAULT_CURRENCY);
|
|
||||||
Session::put(SESSION_LOCALE, $this->language_id ? $this->language->locale : DEFAULT_LOCALE);
|
|
||||||
|
|
||||||
App::setLocale(session(SESSION_LOCALE));
|
$currencyId = ($client && $client->currency_id) ? $client->currency_id : $this->currency_id ?: DEFAULT_CURRENCY;
|
||||||
|
$locale = ($client && $client->language_id) ? $client->language->locale : ($this->language_id ? $this->Language->locale : DEFAULT_LOCALE);
|
||||||
|
|
||||||
|
Session::put(SESSION_CURRENCY, $currencyId);
|
||||||
|
Session::put(SESSION_LOCALE, $locale);
|
||||||
|
|
||||||
|
App::setLocale($locale);
|
||||||
|
|
||||||
|
$format = $this->datetime_format ? $this->datetime_format->format : DEFAULT_DATETIME_FORMAT;
|
||||||
|
if ($this->military_time) {
|
||||||
|
$format = str_replace('g:i a', 'H:i', $format);
|
||||||
|
}
|
||||||
|
Session::put(SESSION_DATETIME_FORMAT, $format);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getInvoiceLabels()
|
public function getInvoiceLabels()
|
||||||
|
@ -183,6 +183,10 @@ class Activity extends Eloquent
|
|||||||
$activity->balance = $invoice->client->balance;
|
$activity->balance = $invoice->client->balance;
|
||||||
$activity->adjustment = $adjustment;
|
$activity->adjustment = $adjustment;
|
||||||
$activity->save();
|
$activity->save();
|
||||||
|
|
||||||
|
// Release any tasks associated with the deleted invoice
|
||||||
|
Task::where('invoice_id', '=', $invoice->id)
|
||||||
|
->update(['invoice_id' => null]);
|
||||||
} else {
|
} else {
|
||||||
$diff = floatval($invoice->amount) - floatval($invoice->getOriginal('amount'));
|
$diff = floatval($invoice->amount) - floatval($invoice->getOriginal('amount'));
|
||||||
|
|
||||||
|
@ -54,6 +54,11 @@ class Client extends EntityModel
|
|||||||
return $this->belongsTo('App\Models\Currency');
|
return $this->belongsTo('App\Models\Currency');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function language()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Language');
|
||||||
|
}
|
||||||
|
|
||||||
public function size()
|
public function size()
|
||||||
{
|
{
|
||||||
return $this->belongsTo('App\Models\Size');
|
return $this->belongsTo('App\Models\Size');
|
||||||
|
@ -34,8 +34,11 @@ class Invitation extends EntityModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
$url = SITE_URL;
|
$url = SITE_URL;
|
||||||
|
$iframe_url = $this->account->iframe_url;
|
||||||
|
|
||||||
if ($this->account->subdomain) {
|
if ($iframe_url) {
|
||||||
|
return "{$iframe_url}?{$this->invitation_key}";
|
||||||
|
} else if ($this->account->subdomain) {
|
||||||
$parsedUrl = parse_url($url);
|
$parsedUrl = parse_url($url);
|
||||||
$host = explode('.', $parsedUrl['host']);
|
$host = explode('.', $parsedUrl['host']);
|
||||||
$subdomain = $host[0];
|
$subdomain = $host[0];
|
||||||
|
@ -11,6 +11,7 @@ class Invoice extends EntityModel
|
|||||||
protected $casts = [
|
protected $casts = [
|
||||||
'is_recurring' => 'boolean',
|
'is_recurring' => 'boolean',
|
||||||
'has_tasks' => 'boolean',
|
'has_tasks' => 'boolean',
|
||||||
|
'auto_bill' => 'boolean',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function account()
|
public function account()
|
||||||
|
@ -14,14 +14,19 @@ class ContactMailer extends Mailer
|
|||||||
{
|
{
|
||||||
public function sendInvoice(Invoice $invoice)
|
public function sendInvoice(Invoice $invoice)
|
||||||
{
|
{
|
||||||
$invoice->load('invitations', 'client', 'account');
|
$invoice->load('invitations', 'client.language', 'account');
|
||||||
$entityType = $invoice->getEntityType();
|
$entityType = $invoice->getEntityType();
|
||||||
|
|
||||||
|
$client = $invoice->client;
|
||||||
|
$account = $invoice->account;
|
||||||
|
|
||||||
|
$account->loadLocalizationSettings($client);
|
||||||
|
|
||||||
$view = 'invoice';
|
$view = 'invoice';
|
||||||
$subject = trans("texts.{$entityType}_subject", ['invoice' => $invoice->invoice_number, 'account' => $invoice->account->getDisplayName()]);
|
$subject = trans("texts.{$entityType}_subject", ['invoice' => $invoice->invoice_number, 'account' => $invoice->account->getDisplayName()]);
|
||||||
$accountName = $invoice->account->getDisplayName();
|
$accountName = $invoice->account->getDisplayName();
|
||||||
$emailTemplate = $invoice->account->getEmailTemplate($entityType);
|
$emailTemplate = $invoice->account->getEmailTemplate($entityType);
|
||||||
$invoiceAmount = Utils::formatMoney($invoice->getRequestedAmount(), $invoice->client->getCurrencyId());
|
$invoiceAmount = Utils::formatMoney($invoice->getRequestedAmount(), $client->getCurrencyId());
|
||||||
|
|
||||||
$this->initClosure($invoice);
|
$this->initClosure($invoice);
|
||||||
|
|
||||||
@ -39,7 +44,7 @@ class ContactMailer extends Mailer
|
|||||||
$variables = [
|
$variables = [
|
||||||
'$footer' => $invoice->account->getEmailFooter(),
|
'$footer' => $invoice->account->getEmailFooter(),
|
||||||
'$link' => $invitation->getLink(),
|
'$link' => $invitation->getLink(),
|
||||||
'$client' => $invoice->client->getDisplayName(),
|
'$client' => $client->getDisplayName(),
|
||||||
'$account' => $accountName,
|
'$account' => $accountName,
|
||||||
'$contact' => $invitation->contact->getDisplayName(),
|
'$contact' => $invitation->contact->getDisplayName(),
|
||||||
'$amount' => $invoiceAmount,
|
'$amount' => $invoiceAmount,
|
||||||
@ -72,6 +77,8 @@ class ContactMailer extends Mailer
|
|||||||
$invoice->save();
|
$invoice->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$account->loadLocalizationSettings();
|
||||||
|
|
||||||
Event::fire(new InvoiceSent($invoice));
|
Event::fire(new InvoiceSent($invoice));
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
@ -79,17 +86,22 @@ class ContactMailer extends Mailer
|
|||||||
|
|
||||||
public function sendPaymentConfirmation(Payment $payment)
|
public function sendPaymentConfirmation(Payment $payment)
|
||||||
{
|
{
|
||||||
|
$account = $payment->account;
|
||||||
|
$client = $payment->client;
|
||||||
|
|
||||||
|
$account->loadLocalizationSettings($client);
|
||||||
|
|
||||||
$invoice = $payment->invoice;
|
$invoice = $payment->invoice;
|
||||||
$view = 'payment_confirmation';
|
$view = 'payment_confirmation';
|
||||||
$subject = trans('texts.payment_subject', ['invoice' => $invoice->invoice_number]);
|
$subject = trans('texts.payment_subject', ['invoice' => $invoice->invoice_number]);
|
||||||
$accountName = $payment->account->getDisplayName();
|
$accountName = $account->getDisplayName();
|
||||||
$emailTemplate = $invoice->account->getEmailTemplate(ENTITY_PAYMENT);
|
$emailTemplate = $account->getEmailTemplate(ENTITY_PAYMENT);
|
||||||
|
|
||||||
$variables = [
|
$variables = [
|
||||||
'$footer' => $payment->account->getEmailFooter(),
|
'$footer' => $account->getEmailFooter(),
|
||||||
'$client' => $payment->client->getDisplayName(),
|
'$client' => $client->getDisplayName(),
|
||||||
'$account' => $accountName,
|
'$account' => $accountName,
|
||||||
'$amount' => Utils::formatMoney($payment->amount, $payment->client->getCurrencyId())
|
'$amount' => Utils::formatMoney($payment->amount, $client->getCurrencyId())
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($payment->invitation) {
|
if ($payment->invitation) {
|
||||||
@ -98,7 +110,7 @@ class ContactMailer extends Mailer
|
|||||||
$variables['$link'] = $payment->invitation->getLink();
|
$variables['$link'] = $payment->invitation->getLink();
|
||||||
} else {
|
} else {
|
||||||
$user = $payment->user;
|
$user = $payment->user;
|
||||||
$contact = $payment->client->contacts[0];
|
$contact = $client->contacts[0];
|
||||||
$variables['$link'] = $payment->invoice->invitations[0]->getLink();
|
$variables['$link'] = $payment->invoice->invitations[0]->getLink();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,6 +121,8 @@ class ContactMailer extends Mailer
|
|||||||
if ($user->email && $contact->email) {
|
if ($user->email && $contact->email) {
|
||||||
$this->sendTo($contact->email, $user->email, $accountName, $subject, $view, $data);
|
$this->sendTo($contact->email, $user->email, $accountName, $subject, $view, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$account->loadLocalizationSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendLicensePaymentConfirmation($name, $email, $amount, $license, $productId)
|
public function sendLicensePaymentConfirmation($name, $email, $amount, $license, $productId)
|
||||||
|
@ -26,8 +26,14 @@ class AccountRepository
|
|||||||
$account->ip = Request::getClientIp();
|
$account->ip = Request::getClientIp();
|
||||||
$account->account_key = str_random(RANDOM_KEY_LENGTH);
|
$account->account_key = str_random(RANDOM_KEY_LENGTH);
|
||||||
|
|
||||||
if (Session::has(SESSION_LOCALE)) {
|
// Track referal code
|
||||||
$locale = Session::get(SESSION_LOCALE);
|
if ($referralCode = Session::get(SESSION_REFERRAL_CODE)) {
|
||||||
|
if ($user = User::whereReferralCode($referralCode)->first()) {
|
||||||
|
$account->referral_user_id = $user->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($locale = Session::get(SESSION_LOCALE)) {
|
||||||
if ($language = Language::whereLocale($locale)->first()) {
|
if ($language = Language::whereLocale($locale)->first()) {
|
||||||
$account->language_id = $language->id;
|
$account->language_id = $language->id;
|
||||||
}
|
}
|
||||||
@ -188,7 +194,7 @@ class AccountRepository
|
|||||||
$accountGateway->user_id = $user->id;
|
$accountGateway->user_id = $user->id;
|
||||||
$accountGateway->gateway_id = NINJA_GATEWAY_ID;
|
$accountGateway->gateway_id = NINJA_GATEWAY_ID;
|
||||||
$accountGateway->public_id = 1;
|
$accountGateway->public_id = 1;
|
||||||
$accountGateway->config = NINJA_GATEWAY_CONFIG;
|
$accountGateway->config = env(NINJA_GATEWAY_CONFIG);
|
||||||
$account->account_gateways()->save($accountGateway);
|
$account->account_gateways()->save($accountGateway);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +212,7 @@ class AccountRepository
|
|||||||
$client->public_id = $account->id;
|
$client->public_id = $account->id;
|
||||||
$client->user_id = $ninjaAccount->users()->first()->id;
|
$client->user_id = $ninjaAccount->users()->first()->id;
|
||||||
$client->currency_id = 1;
|
$client->currency_id = 1;
|
||||||
foreach (['name', 'address1', 'address2', 'city', 'state', 'postal_code', 'country_id', 'work_phone'] as $field) {
|
foreach (['name', 'address1', 'address2', 'city', 'state', 'postal_code', 'country_id', 'work_phone', 'language_id'] as $field) {
|
||||||
$client->$field = $account->$field;
|
$client->$field = $account->$field;
|
||||||
}
|
}
|
||||||
$ninjaAccount->clients()->save($client);
|
$ninjaAccount->clients()->save($client);
|
||||||
|
@ -105,6 +105,9 @@ class ClientRepository
|
|||||||
if (isset($data['currency_id'])) {
|
if (isset($data['currency_id'])) {
|
||||||
$client->currency_id = $data['currency_id'] ? $data['currency_id'] : null;
|
$client->currency_id = $data['currency_id'] ? $data['currency_id'] : null;
|
||||||
}
|
}
|
||||||
|
if (isset($data['language_id'])) {
|
||||||
|
$client->language_id = $data['language_id'] ? $data['language_id'] : null;
|
||||||
|
}
|
||||||
if (isset($data['payment_terms'])) {
|
if (isset($data['payment_terms'])) {
|
||||||
$client->payment_terms = $data['payment_terms'];
|
$client->payment_terms = $data['payment_terms'];
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,15 @@ use App\Models\InvoiceItem;
|
|||||||
use App\Models\Invitation;
|
use App\Models\Invitation;
|
||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
use App\Models\Task;
|
use App\Models\Task;
|
||||||
|
use App\Services\PaymentService;
|
||||||
|
|
||||||
class InvoiceRepository
|
class InvoiceRepository
|
||||||
{
|
{
|
||||||
|
public function __construct(PaymentService $paymentService)
|
||||||
|
{
|
||||||
|
$this->paymentService = $paymentService;
|
||||||
|
}
|
||||||
|
|
||||||
public function getInvoices($accountId, $clientPublicId = false, $entityType = ENTITY_INVOICE, $filter = false)
|
public function getInvoices($accountId, $clientPublicId = false, $entityType = ENTITY_INVOICE, $filter = false)
|
||||||
{
|
{
|
||||||
$query = \DB::table('invoices')
|
$query = \DB::table('invoices')
|
||||||
@ -287,6 +293,12 @@ class InvoiceRepository
|
|||||||
$invoice->start_date = Utils::toSqlDate($data['start_date']);
|
$invoice->start_date = Utils::toSqlDate($data['start_date']);
|
||||||
$invoice->end_date = Utils::toSqlDate($data['end_date']);
|
$invoice->end_date = Utils::toSqlDate($data['end_date']);
|
||||||
$invoice->due_date = null;
|
$invoice->due_date = null;
|
||||||
|
$invoice->auto_bill = isset($data['auto_bill']) && $data['auto_bill'] ? true : false;
|
||||||
|
|
||||||
|
if (isset($data['show_last_sent_date']) && $data['show_last_sent_date']
|
||||||
|
&& isset($data['last_sent_date']) && $data['last_sent_date']) {
|
||||||
|
$invoice->last_sent_date = Utils::toSqlDate($data['last_sent_date']);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$invoice->due_date = isset($data['due_date_sql']) ? $data['due_date_sql'] : Utils::toSqlDate($data['due_date']);
|
$invoice->due_date = isset($data['due_date_sql']) ? $data['due_date_sql'] : Utils::toSqlDate($data['due_date']);
|
||||||
$invoice->frequency_id = 0;
|
$invoice->frequency_id = 0;
|
||||||
@ -635,9 +647,15 @@ class InvoiceRepository
|
|||||||
$invoice->invitations()->save($invitation);
|
$invoice->invitations()->save($invitation);
|
||||||
}
|
}
|
||||||
|
|
||||||
$recurInvoice->last_sent_date = Carbon::now()->toDateTimeString();
|
$recurInvoice->last_sent_date = date('Y-m-d');
|
||||||
$recurInvoice->save();
|
$recurInvoice->save();
|
||||||
|
|
||||||
|
if ($recurInvoice->auto_bill) {
|
||||||
|
if ($this->paymentService->autoBillInvoice($invoice)) {
|
||||||
|
$invoice->invoice_status_id = INVOICE_STATUS_PAID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $invoice;
|
return $invoice;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
204
app/Services/PaymentService.php
Normal file
204
app/Services/PaymentService.php
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
<?php namespace App\Services;
|
||||||
|
|
||||||
|
use URL;
|
||||||
|
use DateTime;
|
||||||
|
use Event;
|
||||||
|
use Omnipay;
|
||||||
|
use Session;
|
||||||
|
use CreditCard;
|
||||||
|
use App\Models\Payment;
|
||||||
|
use App\Models\Account;
|
||||||
|
use App\Models\Country;
|
||||||
|
use App\Models\AccountGatewayToken;
|
||||||
|
use App\Ninja\Repositories\AccountRepository;
|
||||||
|
use App\Events\InvoicePaid;
|
||||||
|
|
||||||
|
class PaymentService {
|
||||||
|
|
||||||
|
public $lastError;
|
||||||
|
|
||||||
|
public function __construct(AccountRepository $accountRepo)
|
||||||
|
{
|
||||||
|
$this->accountRepo = $accountRepo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createGateway($accountGateway)
|
||||||
|
{
|
||||||
|
$gateway = Omnipay::create($accountGateway->gateway->provider);
|
||||||
|
$config = json_decode($accountGateway->config);
|
||||||
|
|
||||||
|
foreach ($config as $key => $val) {
|
||||||
|
if (!$val) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$function = "set".ucfirst($key);
|
||||||
|
$gateway->$function($val);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($accountGateway->gateway->id == GATEWAY_DWOLLA) {
|
||||||
|
if ($gateway->getSandbox() && isset($_ENV['DWOLLA_SANDBOX_KEY']) && isset($_ENV['DWOLLA_SANSBOX_SECRET'])) {
|
||||||
|
$gateway->setKey($_ENV['DWOLLA_SANDBOX_KEY']);
|
||||||
|
$gateway->setSecret($_ENV['DWOLLA_SANSBOX_SECRET']);
|
||||||
|
} elseif (isset($_ENV['DWOLLA_KEY']) && isset($_ENV['DWOLLA_SECRET'])) {
|
||||||
|
$gateway->setKey($_ENV['DWOLLA_KEY']);
|
||||||
|
$gateway->setSecret($_ENV['DWOLLA_SECRET']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $gateway;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPaymentDetails($invitation, $input = null)
|
||||||
|
{
|
||||||
|
$invoice = $invitation->invoice;
|
||||||
|
$account = $invoice->account;
|
||||||
|
$key = $invoice->account_id.'-'.$invoice->invoice_number;
|
||||||
|
$currencyCode = $invoice->client->currency ? $invoice->client->currency->code : ($invoice->account->currency ? $invoice->account->currency->code : 'USD');
|
||||||
|
|
||||||
|
if ($input) {
|
||||||
|
$data = self::convertInputForOmnipay($input);
|
||||||
|
Session::put($key, $data);
|
||||||
|
} elseif (Session::get($key)) {
|
||||||
|
$data = Session::get($key);
|
||||||
|
} else {
|
||||||
|
$data = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$card = new CreditCard($data);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'amount' => $invoice->getRequestedAmount(),
|
||||||
|
'card' => $card,
|
||||||
|
'currency' => $currencyCode,
|
||||||
|
'returnUrl' => URL::to('complete'),
|
||||||
|
'cancelUrl' => $invitation->getLink(),
|
||||||
|
'description' => trans('texts.' . $invoice->getEntityType()) . " {$invoice->invoice_number}",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function convertInputForOmnipay($input)
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'firstName' => $input['first_name'],
|
||||||
|
'lastName' => $input['last_name'],
|
||||||
|
'number' => $input['card_number'],
|
||||||
|
'expiryMonth' => $input['expiration_month'],
|
||||||
|
'expiryYear' => $input['expiration_year'],
|
||||||
|
'cvv' => $input['cvv'],
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isset($input['country_id'])) {
|
||||||
|
$country = Country::find($input['country_id']);
|
||||||
|
|
||||||
|
$data = array_merge($data, [
|
||||||
|
'billingAddress1' => $input['address1'],
|
||||||
|
'billingAddress2' => $input['address2'],
|
||||||
|
'billingCity' => $input['city'],
|
||||||
|
'billingState' => $input['state'],
|
||||||
|
'billingPostcode' => $input['postal_code'],
|
||||||
|
'billingCountry' => $country->iso_3166_2,
|
||||||
|
'shippingAddress1' => $input['address1'],
|
||||||
|
'shippingAddress2' => $input['address2'],
|
||||||
|
'shippingCity' => $input['city'],
|
||||||
|
'shippingState' => $input['state'],
|
||||||
|
'shippingPostcode' => $input['postal_code'],
|
||||||
|
'shippingCountry' => $country->iso_3166_2
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createToken($gateway, $details, $accountGateway, $client, $contactId)
|
||||||
|
{
|
||||||
|
$tokenResponse = $gateway->createCard($details)->send();
|
||||||
|
$cardReference = $tokenResponse->getCardReference();
|
||||||
|
|
||||||
|
if ($cardReference) {
|
||||||
|
$token = AccountGatewayToken::where('client_id', '=', $client->id)
|
||||||
|
->where('account_gateway_id', '=', $accountGateway->id)->first();
|
||||||
|
|
||||||
|
if (!$token) {
|
||||||
|
$token = new AccountGatewayToken();
|
||||||
|
$token->account_id = $client->account->id;
|
||||||
|
$token->contact_id = $contactId;
|
||||||
|
$token->account_gateway_id = $accountGateway->id;
|
||||||
|
$token->client_id = $client->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$token->token = $cardReference;
|
||||||
|
$token->save();
|
||||||
|
} else {
|
||||||
|
$this->lastError = $tokenResponse->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cardReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createPayment($invitation, $ref, $payerId = null)
|
||||||
|
{
|
||||||
|
$invoice = $invitation->invoice;
|
||||||
|
$accountGateway = $invoice->client->account->getGatewayByType(Session::get('payment_type'));
|
||||||
|
|
||||||
|
// sync pro accounts
|
||||||
|
if ($invoice->account->account_key == NINJA_ACCOUNT_KEY
|
||||||
|
&& $invoice->amount == PRO_PLAN_PRICE) {
|
||||||
|
$account = Account::with('users')->find($invoice->client->public_id);
|
||||||
|
if ($account->pro_plan_paid && $account->pro_plan_paid != '0000-00-00') {
|
||||||
|
$date = DateTime::createFromFormat('Y-m-d', $account->pro_plan_paid);
|
||||||
|
$account->pro_plan_paid = $date->modify('+1 year')->format('Y-m-d');
|
||||||
|
} else {
|
||||||
|
$account->pro_plan_paid = date_create()->format('Y-m-d');
|
||||||
|
}
|
||||||
|
$account->save();
|
||||||
|
|
||||||
|
$user = $account->users()->first();
|
||||||
|
$this->accountRepo->syncAccounts($user->id, $account->pro_plan_paid);
|
||||||
|
}
|
||||||
|
|
||||||
|
$payment = Payment::createNew($invitation);
|
||||||
|
$payment->invitation_id = $invitation->id;
|
||||||
|
$payment->account_gateway_id = $accountGateway->id;
|
||||||
|
$payment->invoice_id = $invoice->id;
|
||||||
|
$payment->amount = $invoice->getRequestedAmount();
|
||||||
|
$payment->client_id = $invoice->client_id;
|
||||||
|
$payment->contact_id = $invitation->contact_id;
|
||||||
|
$payment->transaction_reference = $ref;
|
||||||
|
$payment->payment_date = date_create()->format('Y-m-d');
|
||||||
|
|
||||||
|
if ($payerId) {
|
||||||
|
$payment->payer_id = $payerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
$payment->save();
|
||||||
|
|
||||||
|
Event::fire(new InvoicePaid($payment));
|
||||||
|
|
||||||
|
return $payment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function autoBillInvoice($invoice)
|
||||||
|
{
|
||||||
|
$client = $invoice->client;
|
||||||
|
$account = $invoice->account;
|
||||||
|
$invitation = $invoice->invitations->first();
|
||||||
|
$accountGateway = $account->getGatewayConfig(GATEWAY_STRIPE);
|
||||||
|
|
||||||
|
if (!$invitation || !$accountGateway) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup the gateway/payment info
|
||||||
|
$gateway = $this->createGateway($accountGateway);
|
||||||
|
$details = $this->getPaymentDetails($invitation);
|
||||||
|
$details['cardReference'] = $client->getGatewayToken();
|
||||||
|
|
||||||
|
// submit purchase/get response
|
||||||
|
$response = $gateway->purchase($details)->send();
|
||||||
|
$ref = $response->getTransactionReference();
|
||||||
|
|
||||||
|
// create payment record
|
||||||
|
return $this->createPayment($invitation, $ref);
|
||||||
|
}
|
||||||
|
}
|
@ -187,14 +187,11 @@ return [
|
|||||||
'Eloquent' => 'Illuminate\Database\Eloquent\Model',
|
'Eloquent' => 'Illuminate\Database\Eloquent\Model',
|
||||||
'Event' => 'Illuminate\Support\Facades\Event',
|
'Event' => 'Illuminate\Support\Facades\Event',
|
||||||
'File' => 'Illuminate\Support\Facades\File',
|
'File' => 'Illuminate\Support\Facades\File',
|
||||||
//'Form' => 'Illuminate\Support\Facades\Form',
|
|
||||||
'Hash' => 'Illuminate\Support\Facades\Hash',
|
'Hash' => 'Illuminate\Support\Facades\Hash',
|
||||||
//'HTML' => 'Illuminate\Support\Facades\HTML',
|
|
||||||
'Input' => 'Illuminate\Support\Facades\Input',
|
'Input' => 'Illuminate\Support\Facades\Input',
|
||||||
'Lang' => 'Illuminate\Support\Facades\Lang',
|
'Lang' => 'Illuminate\Support\Facades\Lang',
|
||||||
'Log' => 'Illuminate\Support\Facades\Log',
|
'Log' => 'Illuminate\Support\Facades\Log',
|
||||||
'Mail' => 'Illuminate\Support\Facades\Mail',
|
'Mail' => 'Illuminate\Support\Facades\Mail',
|
||||||
//'Paginator' => 'Illuminate\Support\Facades\Paginator',
|
|
||||||
'Password' => 'Illuminate\Support\Facades\Password',
|
'Password' => 'Illuminate\Support\Facades\Password',
|
||||||
'Queue' => 'Illuminate\Support\Facades\Queue',
|
'Queue' => 'Illuminate\Support\Facades\Queue',
|
||||||
'Redirect' => 'Illuminate\Support\Facades\Redirect',
|
'Redirect' => 'Illuminate\Support\Facades\Redirect',
|
||||||
@ -210,41 +207,8 @@ return [
|
|||||||
'Validator' => 'Illuminate\Support\Facades\Validator',
|
'Validator' => 'Illuminate\Support\Facades\Validator',
|
||||||
'View' => 'Illuminate\Support\Facades\View',
|
'View' => 'Illuminate\Support\Facades\View',
|
||||||
|
|
||||||
/*'App' => 'Illuminate\Support\Facades\App',
|
|
||||||
'Artisan' => 'Illuminate\Support\Facades\Artisan',
|
|
||||||
'Auth' => 'Illuminate\Support\Facades\Auth',
|
|
||||||
'Blade' => 'Illuminate\Support\Facades\Blade',
|
|
||||||
'Bus' => 'Illuminate\Support\Facades\Bus',
|
|
||||||
'Cache' => 'Illuminate\Support\Facades\Cache',
|
|
||||||
'Config' => 'Illuminate\Support\Facades\Config',
|
|
||||||
'Cookie' => 'Illuminate\Support\Facades\Cookie',
|
|
||||||
'Crypt' => 'Illuminate\Support\Facades\Crypt',
|
|
||||||
'DB' => 'Illuminate\Support\Facades\DB',
|
|
||||||
'Eloquent' => 'Illuminate\Database\Eloquent\Model',
|
|
||||||
'Event' => 'Illuminate\Support\Facades\Event',
|
|
||||||
'File' => 'Illuminate\Support\Facades\File',
|
|
||||||
'Hash' => 'Illuminate\Support\Facades\Hash',
|
|
||||||
'Input' => 'Illuminate\Support\Facades\Input',
|
|
||||||
'Inspiring' => 'Illuminate\Foundation\Inspiring',
|
|
||||||
'Lang' => 'Illuminate\Support\Facades\Lang',
|
|
||||||
'Log' => 'Illuminate\Support\Facades\Log',
|
|
||||||
'Mail' => 'Illuminate\Support\Facades\Mail',
|
|
||||||
'Password' => 'Illuminate\Support\Facades\Password',
|
|
||||||
'Queue' => 'Illuminate\Support\Facades\Queue',
|
|
||||||
'Redirect' => 'Illuminate\Support\Facades\Redirect',
|
|
||||||
'Redis' => 'Illuminate\Support\Facades\Redis',
|
|
||||||
'Request' => 'Illuminate\Support\Facades\Request',
|
|
||||||
'Response' => 'Illuminate\Support\Facades\Response',
|
|
||||||
'Route' => 'Illuminate\Support\Facades\Route',
|
|
||||||
'Schema' => 'Illuminate\Support\Facades\Schema',
|
|
||||||
'Session' => 'Illuminate\Support\Facades\Session',
|
|
||||||
'Storage' => 'Illuminate\Support\Facades\Storage',
|
|
||||||
'URL' => 'Illuminate\Support\Facades\URL',
|
|
||||||
'Validator' => 'Illuminate\Support\Facades\Validator',
|
|
||||||
'View' => 'Illuminate\Support\Facades\View',*/
|
|
||||||
|
|
||||||
// Added Class Aliases
|
// Added Class Aliases
|
||||||
|
|
||||||
'Utils' => 'App\Libraries\Utils',
|
'Utils' => 'App\Libraries\Utils',
|
||||||
'Form' => 'Collective\Html\FormFacade',
|
'Form' => 'Collective\Html\FormFacade',
|
||||||
'HTML' => 'Collective\Html\HtmlFacade',
|
'HTML' => 'Collective\Html\HtmlFacade',
|
||||||
@ -257,10 +221,8 @@ return [
|
|||||||
'ButtonToolbar' => 'Bootstrapper\Facades\ButtonToolbar',
|
'ButtonToolbar' => 'Bootstrapper\Facades\ButtonToolbar',
|
||||||
'Carousel' => 'Bootstrapper\Facades\Carousel',
|
'Carousel' => 'Bootstrapper\Facades\Carousel',
|
||||||
'DropdownButton' => 'Bootstrapper\Facades\DropdownButton',
|
'DropdownButton' => 'Bootstrapper\Facades\DropdownButton',
|
||||||
//'Form' => 'Bootstrapper\Facades\Form', //need to clarify this guy
|
|
||||||
'Helpers' => 'Bootstrapper\Facades\Helpers',
|
'Helpers' => 'Bootstrapper\Facades\Helpers',
|
||||||
'Icon' => 'Bootstrapper\Facades\Icon',
|
'Icon' => 'Bootstrapper\Facades\Icon',
|
||||||
//'Image' => 'Bootstrapper\Facades\Image',
|
|
||||||
'Label' => 'Bootstrapper\Facades\Label',
|
'Label' => 'Bootstrapper\Facades\Label',
|
||||||
'MediaObject' => 'Bootstrapper\Facades\MediaObject',
|
'MediaObject' => 'Bootstrapper\Facades\MediaObject',
|
||||||
'Navbar' => 'Bootstrapper\Facades\Navbar',
|
'Navbar' => 'Bootstrapper\Facades\Navbar',
|
||||||
|
64
database/migrations/2015_09_07_135935_add_account_domain.php
Normal file
64
database/migrations/2015_09_07_135935_add_account_domain.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
|
||||||
|
class AddAccountDomain extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('accounts', function ($table) {
|
||||||
|
$table->string('iframe_url')->nullable();
|
||||||
|
$table->boolean('military_time')->default(false);
|
||||||
|
$table->unsignedInteger('referral_user_id')->nullable();
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('clients', function ($table) {
|
||||||
|
$table->unsignedInteger('language_id')->nullable();
|
||||||
|
$table->foreign('language_id')->references('id')->on('languages');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('invoices', function ($table) {
|
||||||
|
$table->boolean('auto_bill')->default(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('users', function ($table) {
|
||||||
|
$table->string('referral_code')->nullable();
|
||||||
|
});
|
||||||
|
|
||||||
|
DB::statement('ALTER TABLE invoices MODIFY COLUMN last_sent_date DATE');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('accounts', function ($table) {
|
||||||
|
$table->dropColumn('iframe_url');
|
||||||
|
$table->dropColumn('military_time');
|
||||||
|
$table->dropColumn('referral_user_id');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('clients', function ($table) {
|
||||||
|
$table->dropForeign('clients_language_id_foreign');
|
||||||
|
$table->dropColumn('language_id');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('invoices', function ($table) {
|
||||||
|
$table->dropColumn('auto_bill');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('users', function ($table) {
|
||||||
|
$table->dropColumn('referral_code');
|
||||||
|
});
|
||||||
|
|
||||||
|
DB::statement('ALTER TABLE invoices MODIFY COLUMN last_sent_date TIMESTAMP');
|
||||||
|
}
|
||||||
|
}
|
@ -175,8 +175,8 @@ class PaymentLibrariesSeeder extends Seeder
|
|||||||
'label' => '20-03-2013 6:15 pm'
|
'label' => '20-03-2013 6:15 pm'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'format' => 'm/d/Y H:i',
|
'format' => 'm/d/Y g:i',
|
||||||
'format_moment' => 'MM/DD/YYYY HH:mm:ss',
|
'format_moment' => 'MM/DD/YYYY h:mm:ss',
|
||||||
'label' => '03/20/2013 6:15 pm'
|
'label' => '03/20/2013 6:15 pm'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
BIN
public/images/round_logo.png
Normal file
BIN
public/images/round_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.2 KiB |
@ -30402,17 +30402,25 @@ if (window.ko) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getContactDisplayName(contact)
|
||||||
|
{
|
||||||
|
var str = '';
|
||||||
|
if (contact.first_name || contact.last_name) {
|
||||||
|
str += contact.first_name + ' ' + contact.last_name;
|
||||||
|
}
|
||||||
|
if (str && contact.email) {
|
||||||
|
str += ' - ';
|
||||||
|
}
|
||||||
|
return str + contact.email;
|
||||||
|
}
|
||||||
|
|
||||||
function getClientDisplayName(client)
|
function getClientDisplayName(client)
|
||||||
{
|
{
|
||||||
var contact = client.contacts ? client.contacts[0] : false;
|
var contact = client.contacts ? client.contacts[0] : false;
|
||||||
if (client.name) {
|
if (client.name) {
|
||||||
return client.name;
|
return client.name;
|
||||||
} else if (contact) {
|
} else if (contact) {
|
||||||
if (contact.first_name || contact.last_name) {
|
return getContactDisplayName(contact);
|
||||||
return contact.first_name + ' ' + contact.last_name;
|
|
||||||
} else {
|
|
||||||
return contact.email;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@ -31806,8 +31814,7 @@ NINJA.invoiceLines = function(invoice) {
|
|||||||
|
|
||||||
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
|
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
|
||||||
if (showItemTaxes && tax) {
|
if (showItemTaxes && tax) {
|
||||||
tax = lineTotal * tax / 100;
|
lineTotal += lineTotal * tax / 100;
|
||||||
lineTotal += tax;
|
|
||||||
}
|
}
|
||||||
lineTotal = formatMoney(lineTotal, currencyId);
|
lineTotal = formatMoney(lineTotal, currencyId);
|
||||||
|
|
||||||
@ -31820,7 +31827,7 @@ NINJA.invoiceLines = function(invoice) {
|
|||||||
row.push({style:["quantity", rowStyle], text:qty || ' '});
|
row.push({style:["quantity", rowStyle], text:qty || ' '});
|
||||||
}
|
}
|
||||||
if (showItemTaxes) {
|
if (showItemTaxes) {
|
||||||
row.push({style:["tax", rowStyle], text:tax ? tax.toFixed(2) : ' '});
|
row.push({style:["tax", rowStyle], text:tax ? tax.toString() + '%' : ' '});
|
||||||
}
|
}
|
||||||
row.push({style:["lineTotal", rowStyle], text:lineTotal || ' '});
|
row.push({style:["lineTotal", rowStyle], text:lineTotal || ' '});
|
||||||
|
|
||||||
@ -31987,6 +31994,8 @@ NINJA.clientDetails = function(invoice) {
|
|||||||
|
|
||||||
data = [
|
data = [
|
||||||
{text:clientName || ' ', style: ['clientName']},
|
{text:clientName || ' ', style: ['clientName']},
|
||||||
|
{text:client.id_number},
|
||||||
|
{text:client.vat_number},
|
||||||
{text:client.address1},
|
{text:client.address1},
|
||||||
{text:client.address2},
|
{text:client.address2},
|
||||||
{text:cityStatePostal},
|
{text:cityStatePostal},
|
||||||
|
@ -266,8 +266,7 @@ NINJA.invoiceLines = function(invoice) {
|
|||||||
|
|
||||||
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
|
var lineTotal = roundToTwo(NINJA.parseFloat(item.cost)) * roundToTwo(NINJA.parseFloat(item.qty));
|
||||||
if (showItemTaxes && tax) {
|
if (showItemTaxes && tax) {
|
||||||
tax = lineTotal * tax / 100;
|
lineTotal += lineTotal * tax / 100;
|
||||||
lineTotal += tax;
|
|
||||||
}
|
}
|
||||||
lineTotal = formatMoney(lineTotal, currencyId);
|
lineTotal = formatMoney(lineTotal, currencyId);
|
||||||
|
|
||||||
@ -280,7 +279,7 @@ NINJA.invoiceLines = function(invoice) {
|
|||||||
row.push({style:["quantity", rowStyle], text:qty || ' '});
|
row.push({style:["quantity", rowStyle], text:qty || ' '});
|
||||||
}
|
}
|
||||||
if (showItemTaxes) {
|
if (showItemTaxes) {
|
||||||
row.push({style:["tax", rowStyle], text:tax ? tax.toFixed(2) : ' '});
|
row.push({style:["tax", rowStyle], text:(tax ? tax.toString() + '%') : ' '});
|
||||||
}
|
}
|
||||||
row.push({style:["lineTotal", rowStyle], text:lineTotal || ' '});
|
row.push({style:["lineTotal", rowStyle], text:lineTotal || ' '});
|
||||||
|
|
||||||
@ -447,6 +446,8 @@ NINJA.clientDetails = function(invoice) {
|
|||||||
|
|
||||||
data = [
|
data = [
|
||||||
{text:clientName || ' ', style: ['clientName']},
|
{text:clientName || ' ', style: ['clientName']},
|
||||||
|
{text:client.id_number},
|
||||||
|
{text:client.vat_number},
|
||||||
{text:client.address1},
|
{text:client.address1},
|
||||||
{text:client.address2},
|
{text:client.address2},
|
||||||
{text:cityStatePostal},
|
{text:cityStatePostal},
|
||||||
|
@ -524,17 +524,25 @@ if (window.ko) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getContactDisplayName(contact)
|
||||||
|
{
|
||||||
|
var str = '';
|
||||||
|
if (contact.first_name || contact.last_name) {
|
||||||
|
str += contact.first_name + ' ' + contact.last_name;
|
||||||
|
}
|
||||||
|
if (str && contact.email) {
|
||||||
|
str += ' - ';
|
||||||
|
}
|
||||||
|
return str + contact.email;
|
||||||
|
}
|
||||||
|
|
||||||
function getClientDisplayName(client)
|
function getClientDisplayName(client)
|
||||||
{
|
{
|
||||||
var contact = client.contacts ? client.contacts[0] : false;
|
var contact = client.contacts ? client.contacts[0] : false;
|
||||||
if (client.name) {
|
if (client.name) {
|
||||||
return client.name;
|
return client.name;
|
||||||
} else if (contact) {
|
} else if (contact) {
|
||||||
if (contact.first_name || contact.last_name) {
|
return getContactDisplayName(contact);
|
||||||
return contact.first_name + ' ' + contact.last_name;
|
|
||||||
} else {
|
|
||||||
return contact.email;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,8 @@ If you'd like to translate the site please use [caouecs/Laravel4-long](https://g
|
|||||||
|
|
||||||
* Built using Laravel 5
|
* Built using Laravel 5
|
||||||
* Live PDF generation using [pdfmake](http://pdfmake.org/)
|
* Live PDF generation using [pdfmake](http://pdfmake.org/)
|
||||||
* Integrates with 30+ payment providers
|
* Integrates with 30+ payment providers with [OmniPay](https://github.com/thephpleague/omnipay)
|
||||||
* Recurring invoices
|
* Recurring invoices with auto-billing
|
||||||
* Tasks with time-tracking
|
* Tasks with time-tracking
|
||||||
* Multi-user/multi-company support
|
* Multi-user/multi-company support
|
||||||
* Tax rates and payment terms
|
* Tax rates and payment terms
|
||||||
|
@ -768,5 +768,14 @@
|
|||||||
'status_paid' => 'Paid',
|
'status_paid' => 'Paid',
|
||||||
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
||||||
|
|
||||||
|
'iframe_url' => 'Website',
|
||||||
|
'iframe_url_help1' => 'Copy the following code to a page on your site.',
|
||||||
|
'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.',
|
||||||
|
|
||||||
|
'auto_bill' => 'Auto Bill',
|
||||||
|
'military_time' => '24 Hour Time',
|
||||||
|
'last_sent' => 'Last Sent',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
@ -767,6 +767,14 @@ return array(
|
|||||||
'status_paid' => 'Paid',
|
'status_paid' => 'Paid',
|
||||||
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
||||||
|
|
||||||
|
'iframe_url' => 'Website',
|
||||||
|
'iframe_url_help1' => 'Copy the following code to a page on your site.',
|
||||||
|
'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.',
|
||||||
|
|
||||||
|
'auto_bill' => 'Auto Bill',
|
||||||
|
'military_time' => '24 Hour Time',
|
||||||
|
'last_sent' => 'Last Sent',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
||||||
|
@ -35,7 +35,7 @@ return array(
|
|||||||
'invoice_number_short' => 'Invoice #',
|
'invoice_number_short' => 'Invoice #',
|
||||||
'po_number' => 'PO Number',
|
'po_number' => 'PO Number',
|
||||||
'po_number_short' => 'PO #',
|
'po_number_short' => 'PO #',
|
||||||
'frequency_id' => 'How often',
|
'frequency_id' => 'How Often',
|
||||||
'discount' => 'Discount',
|
'discount' => 'Discount',
|
||||||
'taxes' => 'Taxes',
|
'taxes' => 'Taxes',
|
||||||
'tax' => 'Tax',
|
'tax' => 'Tax',
|
||||||
@ -207,7 +207,7 @@ return array(
|
|||||||
'client_will_create' => 'client will be created',
|
'client_will_create' => 'client will be created',
|
||||||
'clients_will_create' => 'clients will be created',
|
'clients_will_create' => 'clients will be created',
|
||||||
'email_settings' => 'Email Settings',
|
'email_settings' => 'Email Settings',
|
||||||
'pdf_email_attachment' => 'Attach to Emails',
|
'pdf_email_attachment' => 'Attach PDFs',
|
||||||
|
|
||||||
// application messages
|
// application messages
|
||||||
'created_client' => 'Successfully created client',
|
'created_client' => 'Successfully created client',
|
||||||
@ -767,5 +767,14 @@ return array(
|
|||||||
'status_paid' => 'Paid',
|
'status_paid' => 'Paid',
|
||||||
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
||||||
|
|
||||||
|
'iframe_url' => 'Website',
|
||||||
|
'iframe_url_help1' => 'Copy the following code to a page on your site.',
|
||||||
|
'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.',
|
||||||
|
|
||||||
|
'auto_bill' => 'Auto Bill',
|
||||||
|
'military_time' => '24 Hour Time',
|
||||||
|
'last_sent' => 'Last Sent',
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -745,6 +745,15 @@ return array(
|
|||||||
'status_paid' => 'Paid',
|
'status_paid' => 'Paid',
|
||||||
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
||||||
|
|
||||||
|
'iframe_url' => 'Website',
|
||||||
|
'iframe_url_help1' => 'Copy the following code to a page on your site.',
|
||||||
|
'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.',
|
||||||
|
|
||||||
|
'auto_bill' => 'Auto Bill',
|
||||||
|
'military_time' => '24 Hour Time',
|
||||||
|
'last_sent' => 'Last Sent',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
@ -767,6 +767,14 @@ return array(
|
|||||||
'status_paid' => 'Paid',
|
'status_paid' => 'Paid',
|
||||||
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
||||||
|
|
||||||
|
'iframe_url' => 'Website',
|
||||||
|
'iframe_url_help1' => 'Copy the following code to a page on your site.',
|
||||||
|
'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.',
|
||||||
|
|
||||||
|
'auto_bill' => 'Auto Bill',
|
||||||
|
'military_time' => '24 Hour Time',
|
||||||
|
'last_sent' => 'Last Sent',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
@ -759,6 +759,15 @@ return array(
|
|||||||
'status_paid' => 'Paid',
|
'status_paid' => 'Paid',
|
||||||
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
||||||
|
|
||||||
|
'iframe_url' => 'Website',
|
||||||
|
'iframe_url_help1' => 'Copy the following code to a page on your site.',
|
||||||
|
'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.',
|
||||||
|
|
||||||
|
'auto_bill' => 'Auto Bill',
|
||||||
|
'military_time' => '24 Hour Time',
|
||||||
|
'last_sent' => 'Last Sent',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
||||||
|
@ -760,6 +760,14 @@ return array(
|
|||||||
'status_paid' => 'Paid',
|
'status_paid' => 'Paid',
|
||||||
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
||||||
|
|
||||||
|
'iframe_url' => 'Website',
|
||||||
|
'iframe_url_help1' => 'Copy the following code to a page on your site.',
|
||||||
|
'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.',
|
||||||
|
|
||||||
|
'auto_bill' => 'Auto Bill',
|
||||||
|
'military_time' => '24 Hour Time',
|
||||||
|
'last_sent' => 'Last Sent',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -762,6 +762,14 @@ return array(
|
|||||||
'status_paid' => 'Paid',
|
'status_paid' => 'Paid',
|
||||||
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
||||||
|
|
||||||
|
'iframe_url' => 'Website',
|
||||||
|
'iframe_url_help1' => 'Copy the following code to a page on your site.',
|
||||||
|
'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.',
|
||||||
|
|
||||||
|
'auto_bill' => 'Auto Bill',
|
||||||
|
'military_time' => '24 Hour Time',
|
||||||
|
'last_sent' => 'Last Sent',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
||||||
|
@ -769,6 +769,14 @@ return array(
|
|||||||
'status_paid' => 'Paid',
|
'status_paid' => 'Paid',
|
||||||
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
||||||
|
|
||||||
|
'iframe_url' => 'Website',
|
||||||
|
'iframe_url_help1' => 'Copy the following code to a page on your site.',
|
||||||
|
'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.',
|
||||||
|
|
||||||
|
'auto_bill' => 'Auto Bill',
|
||||||
|
'military_time' => '24 Hour Time',
|
||||||
|
'last_sent' => 'Last Sent',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -767,6 +767,14 @@ return array(
|
|||||||
'status_paid' => 'Paid',
|
'status_paid' => 'Paid',
|
||||||
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
||||||
|
|
||||||
|
'iframe_url' => 'Website',
|
||||||
|
'iframe_url_help1' => 'Copy the following code to a page on your site.',
|
||||||
|
'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.',
|
||||||
|
|
||||||
|
'auto_bill' => 'Auto Bill',
|
||||||
|
'military_time' => '24 Hour Time',
|
||||||
|
'last_sent' => 'Last Sent',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
@ -762,6 +762,14 @@ return array(
|
|||||||
'status_paid' => 'Paid',
|
'status_paid' => 'Paid',
|
||||||
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
||||||
|
|
||||||
|
'iframe_url' => 'Website',
|
||||||
|
'iframe_url_help1' => 'Copy the following code to a page on your site.',
|
||||||
|
'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.',
|
||||||
|
|
||||||
|
'auto_bill' => 'Auto Bill',
|
||||||
|
'military_time' => '24 Hour Time',
|
||||||
|
'last_sent' => 'Last Sent',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
||||||
|
@ -762,5 +762,14 @@ return array(
|
|||||||
'status_paid' => 'Paid',
|
'status_paid' => 'Paid',
|
||||||
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
||||||
|
|
||||||
|
'iframe_url' => 'Website',
|
||||||
|
'iframe_url_help1' => 'Copy the following code to a page on your site.',
|
||||||
|
'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.',
|
||||||
|
|
||||||
|
'auto_bill' => 'Auto Bill',
|
||||||
|
'military_time' => '24 Hour Time',
|
||||||
|
'last_sent' => 'Last Sent',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
||||||
|
@ -765,6 +765,14 @@ return array(
|
|||||||
'status_paid' => 'Paid',
|
'status_paid' => 'Paid',
|
||||||
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
'show_line_item_tax' => 'Display <b>line item taxes</b> inline',
|
||||||
|
|
||||||
|
'iframe_url' => 'Website',
|
||||||
|
'iframe_url_help1' => 'Copy the following code to a page on your site.',
|
||||||
|
'iframe_url_help2' => 'You can test the feature by clicking \'View as recipient\' for an invoice.',
|
||||||
|
|
||||||
|
'auto_bill' => 'Auto Bill',
|
||||||
|
'military_time' => '24 Hour Time',
|
||||||
|
'last_sent' => 'Last Sent',
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
)) !!}
|
)) !!}
|
||||||
|
|
||||||
{{ Former::populate($account) }}
|
{{ Former::populate($account) }}
|
||||||
|
{{ Former::populateField('military_time', intval($account->military_time)) }}
|
||||||
@if ($showUser)
|
@if ($showUser)
|
||||||
{{ Former::populateField('first_name', $primaryUser->first_name) }}
|
{{ Former::populateField('first_name', $primaryUser->first_name) }}
|
||||||
{{ Former::populateField('last_name', $primaryUser->last_name) }}
|
{{ Former::populateField('last_name', $primaryUser->last_name) }}
|
||||||
@ -37,12 +38,6 @@
|
|||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
|
||||||
{!! Former::text('name') !!}
|
{!! Former::text('name') !!}
|
||||||
|
|
||||||
@if (Auth::user()->isPro() && Utils::isNinja())
|
|
||||||
{{ Former::setOption('capitalize_translations', false) }}
|
|
||||||
{!! Former::text('subdomain')->placeholder('texts.www')->onchange('onSubdomainChange()') !!}
|
|
||||||
@endif
|
|
||||||
|
|
||||||
{!! Former::text('id_number') !!}
|
{!! Former::text('id_number') !!}
|
||||||
{!! Former::text('vat_number') !!}
|
{!! Former::text('vat_number') !!}
|
||||||
{!! Former::text('work_email') !!}
|
{!! Former::text('work_email') !!}
|
||||||
@ -111,16 +106,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
|
||||||
{!! Former::select('language_id')->addOption('','')
|
|
||||||
->fromQuery($languages, 'name', 'id') !!}
|
|
||||||
{!! Former::select('currency_id')->addOption('','')
|
{!! Former::select('currency_id')->addOption('','')
|
||||||
->fromQuery($currencies, 'name', 'id') !!}
|
->fromQuery($currencies, 'name', 'id') !!}
|
||||||
|
{!! Former::select('language_id')->addOption('','')
|
||||||
|
->fromQuery($languages, 'name', 'id') !!}
|
||||||
{!! Former::select('timezone_id')->addOption('','')
|
{!! Former::select('timezone_id')->addOption('','')
|
||||||
->fromQuery($timezones, 'location', 'id') !!}
|
->fromQuery($timezones, 'location', 'id') !!}
|
||||||
{!! Former::select('date_format_id')->addOption('','')
|
{!! Former::select('date_format_id')->addOption('','')
|
||||||
->fromQuery($dateFormats, 'label', 'id') !!}
|
->fromQuery($dateFormats, 'label', 'id') !!}
|
||||||
{!! Former::select('datetime_format_id')->addOption('','')
|
{!! Former::select('datetime_format_id')->addOption('','')
|
||||||
->fromQuery($datetimeFormats, 'label', 'id') !!}
|
->fromQuery($datetimeFormats, 'label', 'id') !!}
|
||||||
|
{!! Former::checkbox('military_time')->text(trans('texts.enable')) !!}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -286,15 +283,6 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSubdomainChange() {
|
|
||||||
var input = $('#subdomain');
|
|
||||||
var val = input.val();
|
|
||||||
if (!val) return;
|
|
||||||
val = val.replace(/[^a-zA-Z0-9_\-]/g, '').toLowerCase().substring(0, {{ MAX_SUBDOMAIN_LENGTH }});
|
|
||||||
input.val(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@stop
|
@stop
|
||||||
|
@ -67,6 +67,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
|
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<h3 class="panel-title">{!! trans('texts.email_settings') !!}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
{{ Former::setOption('capitalize_translations', false) }}
|
||||||
|
{!! Former::text('subdomain')->placeholder(trans('texts.www'))->onchange('onSubdomainChange()') !!}
|
||||||
|
{!! Former::text('iframe_url')->placeholder('http://invoices.example.com/')
|
||||||
|
->onchange('onDomainChange()')->appendIcon('question-sign')->addGroupClass('iframe_url') !!}
|
||||||
|
{!! Former::checkbox('pdf_email_attachment')->text(trans('texts.enable')) !!}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h3 class="panel-title">{!! trans('texts.invoice_number') !!}</h3>
|
<h3 class="panel-title">{!! trans('texts.invoice_number') !!}</h3>
|
||||||
@ -90,14 +103,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h3 class="panel-title">{!! trans('texts.pdf_settings') !!}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
{!! Former::checkbox('pdf_email_attachment')->text(trans('texts.enable')) !!}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -114,6 +119,35 @@
|
|||||||
</script>
|
</script>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
|
||||||
|
<div class="modal fade" id="iframeHelpModal" tabindex="-1" role="dialog" aria-labelledby="iframeHelpModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog" style="min-width:150px">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
<h4 class="modal-title" id="iframeHelpModalLabel">{{ trans('texts.iframe_url') }}</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>{{ trans('texts.iframe_url_help1') }}</p>
|
||||||
|
<pre><iframe id="iFrame" width="800" height="1000"></iframe>
|
||||||
|
<script language="javascript">
|
||||||
|
var iframe = document.getElementById('iFrame');
|
||||||
|
iframe.src = '{{ SITE_URL }}/view/'
|
||||||
|
+ window.location.search.substring(1);
|
||||||
|
</script></pre>
|
||||||
|
<p>{{ trans('texts.iframe_url_help2') }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer" style="margin-top: 0px">
|
||||||
|
<button type="button" class="btn btn-primary" data-dismiss="modal">{{ trans('texts.close') }}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{!! Former::close() !!}
|
{!! Former::close() !!}
|
||||||
|
|
||||||
|
|
||||||
@ -125,6 +159,22 @@
|
|||||||
$('#quote_number_counter').val(disabled ? '' : '{!! $account->quote_number_counter !!}');
|
$('#quote_number_counter').val(disabled ? '' : '{!! $account->quote_number_counter !!}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onSubdomainChange() {
|
||||||
|
var input = $('#subdomain');
|
||||||
|
var val = input.val();
|
||||||
|
if (!val) return;
|
||||||
|
val = val.replace(/[^a-zA-Z0-9_\-]/g, '').toLowerCase().substring(0, {{ MAX_SUBDOMAIN_LENGTH }});
|
||||||
|
input.val(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDomainChange() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.iframe_url .input-group-addon').click(function() {
|
||||||
|
$('#iframeHelpModal').modal('show');
|
||||||
|
});
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
setQuoteNumberEnabled();
|
setQuoteNumberEnabled();
|
||||||
});
|
});
|
||||||
|
@ -102,7 +102,11 @@
|
|||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
|
||||||
{!! Former::select('currency_id')->addOption('','')
|
{!! Former::select('currency_id')->addOption('','')
|
||||||
|
->placeholder($account->currency ? $account->currency->name : '')
|
||||||
->fromQuery($currencies, 'name', 'id') !!}
|
->fromQuery($currencies, 'name', 'id') !!}
|
||||||
|
{!! Former::select('language_id')->addOption('','')
|
||||||
|
->placeholder($account->language ? $account->language->name : '')
|
||||||
|
->fromQuery($languages, 'name', 'id') !!}
|
||||||
{!! Former::select('payment_terms')->addOption('','')
|
{!! Former::select('payment_terms')->addOption('','')
|
||||||
->fromQuery($paymentTerms, 'name', 'num_days')
|
->fromQuery($paymentTerms, 'name', 'num_days')
|
||||||
->help(trans('texts.payment_terms_help')) !!}
|
->help(trans('texts.payment_terms_help')) !!}
|
||||||
|
@ -99,6 +99,10 @@
|
|||||||
<p>{!! $client->getWebsite() !!}</p>
|
<p>{!! $client->getWebsite() !!}</p>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
@if ($client->language)
|
||||||
|
<p><i class="fa fa-language" style="width: 20px"></i>{{ $client->language->name }}</p>
|
||||||
|
@endif
|
||||||
|
|
||||||
<p>{{ $client->payment_terms ? trans('texts.payment_terms') . ": Net " . $client->payment_terms : '' }}</p>
|
<p>{{ $client->payment_terms ? trans('texts.payment_terms') . ": Net " . $client->payment_terms : '' }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
<div style="display:none">
|
<div style="display:none">
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
{!! Former::select('client')->addOption('', '')->data_bind("dropdown: client")->addGroupClass('client_select closer-row') !!}
|
{!! Former::select('client')->addOption('', '')->data_bind("dropdown: client")->addClass('client-input')->addGroupClass('client_select closer-row') !!}
|
||||||
|
|
||||||
<div class="form-group" style="margin-bottom: 8px">
|
<div class="form-group" style="margin-bottom: 8px">
|
||||||
<div class="col-lg-8 col-sm-8 col-lg-offset-4 col-sm-offset-4">
|
<div class="col-lg-8 col-sm-8 col-lg-offset-4 col-sm-offset-4">
|
||||||
@ -101,10 +101,20 @@
|
|||||||
{!! trans('texts.created_by_invoice', ['invoice' => link_to('/invoices/'.$invoice->recurring_invoice->public_id, trans('texts.recurring_invoice'))]) !!}
|
{!! trans('texts.created_by_invoice', ['invoice' => link_to('/invoices/'.$invoice->recurring_invoice->public_id, trans('texts.recurring_invoice'))]) !!}
|
||||||
</div>
|
</div>
|
||||||
@elseif ($invoice && isset($lastSent) && $lastSent)
|
@elseif ($invoice && isset($lastSent) && $lastSent)
|
||||||
<div class="pull-right" style="padding-top: 6px">
|
<div class="form-group" data-bind="visible: !show_last_sent_date()" >
|
||||||
{!! trans('texts.last_invoice_sent', [
|
<label for="client" class="control-label col-lg-4 col-sm-4">{{ trans('texts.last_sent') }}</label>
|
||||||
'date' => link_to('/invoices/'.$lastSent->public_id, Utils::dateToString($invoice->last_sent_date), ['id' => 'lastInvoiceSent'])
|
<div class="col-lg-8 col-sm-8">
|
||||||
]) !!}
|
<div style="padding-top:10px">
|
||||||
|
<a href="#" data-bind="click: $root.clickLastSentDate">{{ Utils::dateToString($invoice->last_sent_date) }}</a> -
|
||||||
|
{!! link_to('/invoices/'.$lastSent->public_id, trans('texts.view_invoice'), ['id' => 'lastInvoiceSent', 'target' => '_blank']) !!}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div data-bind="visible: show_last_sent_date()" style="display:none">
|
||||||
|
{!! Former::text('last_sent_date')->data_bind("datePicker: last_sent_date, valueUpdate: 'afterkeydown', visible: show_last_sent_date()")
|
||||||
|
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))
|
||||||
|
->label(trans('texts.last_sent'))
|
||||||
|
->appendIcon('calendar')->addGroupClass('last_sent_date') !!}
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
@ -113,7 +123,15 @@
|
|||||||
|
|
||||||
<div class="col-md-4" id="col_2">
|
<div class="col-md-4" id="col_2">
|
||||||
<span data-bind="visible: !is_recurring()">
|
<span data-bind="visible: !is_recurring()">
|
||||||
{!! Former::text('invoice_number')->label(trans("texts.{$entityType}_number_short"))->data_bind("value: invoice_number, valueUpdate: 'afterkeydown'") !!}
|
{!! Former::text('invoice_number')
|
||||||
|
->label(trans("texts.{$entityType}_number_short"))
|
||||||
|
->data_bind("value: invoice_number, valueUpdate: 'afterkeydown'") !!}
|
||||||
|
</span>
|
||||||
|
<span data-bind="visible: is_recurring()" style="display: none">
|
||||||
|
{!! Former::checkbox('auto_bill')
|
||||||
|
->label(trans('texts.auto_bill'))
|
||||||
|
->text(trans('texts.enable'))
|
||||||
|
->data_bind("checked: auto_bill, valueUpdate: 'afterkeydown'") !!}
|
||||||
</span>
|
</span>
|
||||||
{!! Former::text('po_number')->label(trans('texts.po_number_short'))->data_bind("value: po_number, valueUpdate: 'afterkeydown'") !!}
|
{!! Former::text('po_number')->label(trans('texts.po_number_short'))->data_bind("value: po_number, valueUpdate: 'afterkeydown'") !!}
|
||||||
{!! Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'")
|
{!! Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'")
|
||||||
@ -124,7 +142,7 @@
|
|||||||
|
|
||||||
<div class="form-group" style="margin-bottom: 8px">
|
<div class="form-group" style="margin-bottom: 8px">
|
||||||
<label for="taxes" class="control-label col-lg-4 col-sm-4">{{ trans('texts.taxes') }}</label>
|
<label for="taxes" class="control-label col-lg-4 col-sm-4">{{ trans('texts.taxes') }}</label>
|
||||||
<div class="col-lg-8 col-sm-8" style="padding-top: 7px">
|
<div class="col-lg-8 col-sm-8" style="padding-top: 10px">
|
||||||
<a href="#" data-bind="click: $root.showTaxesForm"><i class="glyphicon glyphicon-list-alt"></i> {{ trans('texts.manage_rates') }}</a>
|
<a href="#" data-bind="click: $root.showTaxesForm"><i class="glyphicon glyphicon-list-alt"></i> {{ trans('texts.manage_rates') }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -439,10 +457,16 @@
|
|||||||
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{!! Former::select('currency_id')->addOption('','')->data_bind('value: currency_id')
|
{!! Former::select('currency_id')->addOption('','')
|
||||||
|
->placeholder($account->currency ? $account->currency->name : '')
|
||||||
|
->data_bind('value: currency_id')
|
||||||
->fromQuery($currencies, 'name', 'id') !!}
|
->fromQuery($currencies, 'name', 'id') !!}
|
||||||
|
|
||||||
<span data-bind="visible: $root.showMore">
|
<span data-bind="visible: $root.showMore">
|
||||||
|
{!! Former::select('language_id')->addOption('','')
|
||||||
|
->placeholder($account->language ? $account->language->name : '')
|
||||||
|
->data_bind('value: language_id')
|
||||||
|
->fromQuery($languages, 'name', 'id') !!}
|
||||||
{!! Former::select('payment_terms')->addOption('','')->data_bind('value: payment_terms')
|
{!! Former::select('payment_terms')->addOption('','')->data_bind('value: payment_terms')
|
||||||
->fromQuery($paymentTerms, 'name', 'num_days')
|
->fromQuery($paymentTerms, 'name', 'num_days')
|
||||||
->help(trans('texts.payment_terms_help')) !!}
|
->help(trans('texts.payment_terms_help')) !!}
|
||||||
@ -569,7 +593,7 @@
|
|||||||
|
|
||||||
$('[rel=tooltip]').tooltip({'trigger':'manual'});
|
$('[rel=tooltip]').tooltip({'trigger':'manual'});
|
||||||
|
|
||||||
$('#invoice_date, #due_date, #start_date, #end_date').datepicker();
|
$('#invoice_date, #due_date, #start_date, #end_date, #last_sent_date').datepicker();
|
||||||
|
|
||||||
@if ($client && !$invoice)
|
@if ($client && !$invoice)
|
||||||
$('input[name=client]').val({{ $client->public_id }});
|
$('input[name=client]').val({{ $client->public_id }});
|
||||||
@ -586,6 +610,8 @@
|
|||||||
var clientId = parseInt($('input[name=client]').val(), 10);
|
var clientId = parseInt($('input[name=client]').val(), 10);
|
||||||
if (clientId > 0) {
|
if (clientId > 0) {
|
||||||
model.loadClient(clientMap[clientId]);
|
model.loadClient(clientMap[clientId]);
|
||||||
|
// we enable searching by contact but the selection must be the client
|
||||||
|
$('.client-input').val(getClientDisplayName(clientMap[clientId]));
|
||||||
} else {
|
} else {
|
||||||
model.loadClient($.parseJSON(ko.toJSON(new ClientModel())));
|
model.loadClient($.parseJSON(ko.toJSON(new ClientModel())));
|
||||||
model.invoice().client().country = false;
|
model.invoice().client().country = false;
|
||||||
@ -610,7 +636,7 @@
|
|||||||
showLearnMore();
|
showLearnMore();
|
||||||
});
|
});
|
||||||
|
|
||||||
var fields = ['invoice_date', 'due_date', 'start_date', 'end_date'];
|
var fields = ['invoice_date', 'due_date', 'start_date', 'end_date', 'last_sent_date'];
|
||||||
for (var i=0; i<fields.length; i++) {
|
for (var i=0; i<fields.length; i++) {
|
||||||
var field = fields[i];
|
var field = fields[i];
|
||||||
(function (_field) {
|
(function (_field) {
|
||||||
@ -796,7 +822,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (confirm('{!! trans("texts.confirm_recurring_email_$entityType") !!}' + '\n\n' + getSendToEmails() + '\n' + '{!! trans("texts.confirm_recurring_timing") !!}')) {
|
if (confirm("{!! trans("texts.confirm_recurring_email_$entityType") !!}" + '\n\n' + getSendToEmails() + '\n' + "{!! trans("texts.confirm_recurring_timing") !!}")) {
|
||||||
submitAction('');
|
submitAction('');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -936,10 +962,15 @@
|
|||||||
function ViewModel(data) {
|
function ViewModel(data) {
|
||||||
var self = this;
|
var self = this;
|
||||||
self.showMore = ko.observable(false);
|
self.showMore = ko.observable(false);
|
||||||
|
|
||||||
//self.invoice = data ? false : new InvoiceModel();
|
//self.invoice = data ? false : new InvoiceModel();
|
||||||
self.invoice = ko.observable(data ? false : new InvoiceModel());
|
self.invoice = ko.observable(data ? false : new InvoiceModel());
|
||||||
self.tax_rates = ko.observableArray();
|
self.tax_rates = ko.observableArray();
|
||||||
|
|
||||||
|
self.clickLastSentDate = function() {
|
||||||
|
self.invoice().show_last_sent_date(true);
|
||||||
|
}
|
||||||
|
|
||||||
self.loadClient = function(client) {
|
self.loadClient = function(client) {
|
||||||
ko.mapping.fromJS(client, model.invoice().client().mapping, model.invoice().client);
|
ko.mapping.fromJS(client, model.invoice().client().mapping, model.invoice().client);
|
||||||
@if (!$invoice)
|
@if (!$invoice)
|
||||||
@ -1175,9 +1206,11 @@
|
|||||||
self.due_date = ko.observable('');
|
self.due_date = ko.observable('');
|
||||||
self.start_date = ko.observable('{{ Utils::today() }}');
|
self.start_date = ko.observable('{{ Utils::today() }}');
|
||||||
self.end_date = ko.observable('');
|
self.end_date = ko.observable('');
|
||||||
|
self.last_sent_date = ko.observable('');
|
||||||
self.tax_name = ko.observable();
|
self.tax_name = ko.observable();
|
||||||
self.tax_rate = ko.observable();
|
self.tax_rate = ko.observable();
|
||||||
self.is_recurring = ko.observable({{ $isRecurring ? 'true' : 'false' }});
|
self.is_recurring = ko.observable({{ $isRecurring ? 'true' : 'false' }});
|
||||||
|
self.auto_bill = ko.observable();
|
||||||
self.invoice_status_id = ko.observable(0);
|
self.invoice_status_id = ko.observable(0);
|
||||||
self.invoice_items = ko.observableArray();
|
self.invoice_items = ko.observableArray();
|
||||||
self.amount = ko.observable(0);
|
self.amount = ko.observable(0);
|
||||||
@ -1185,6 +1218,7 @@
|
|||||||
self.invoice_design_id = ko.observable({{ $account->invoice_design_id }});
|
self.invoice_design_id = ko.observable({{ $account->invoice_design_id }});
|
||||||
self.partial = ko.observable(0);
|
self.partial = ko.observable(0);
|
||||||
self.has_tasks = ko.observable(false);
|
self.has_tasks = ko.observable(false);
|
||||||
|
self.show_last_sent_date = ko.observable(false);
|
||||||
|
|
||||||
self.custom_value1 = ko.observable(0);
|
self.custom_value1 = ko.observable(0);
|
||||||
self.custom_value2 = ko.observable(0);
|
self.custom_value2 = ko.observable(0);
|
||||||
@ -1489,6 +1523,7 @@
|
|||||||
self.size_id = ko.observable('');
|
self.size_id = ko.observable('');
|
||||||
self.industry_id = ko.observable('');
|
self.industry_id = ko.observable('');
|
||||||
self.currency_id = ko.observable('');
|
self.currency_id = ko.observable('');
|
||||||
|
self.language_id = ko.observable('');
|
||||||
self.website = ko.observable('');
|
self.website = ko.observable('');
|
||||||
self.payment_terms = ko.observable(0);
|
self.payment_terms = ko.observable(0);
|
||||||
self.contacts = ko.observableArray();
|
self.contacts = ko.observableArray();
|
||||||
@ -1814,14 +1849,19 @@
|
|||||||
|
|
||||||
for (var i=0; i<clients.length; i++) {
|
for (var i=0; i<clients.length; i++) {
|
||||||
var client = clients[i];
|
var client = clients[i];
|
||||||
|
var clientName = getClientDisplayName(client);
|
||||||
for (var j=0; j<client.contacts.length; j++) {
|
for (var j=0; j<client.contacts.length; j++) {
|
||||||
var contact = client.contacts[j];
|
var contact = client.contacts[j];
|
||||||
|
var contactName = getContactDisplayName(contact);
|
||||||
if (contact.is_primary) {
|
if (contact.is_primary) {
|
||||||
contact.send_invoice = true;
|
contact.send_invoice = true;
|
||||||
}
|
}
|
||||||
|
if (clientName != contactName) {
|
||||||
|
$clientSelect.append(new Option(contactName, client.public_id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
clientMap[client.public_id] = client;
|
clientMap[client.public_id] = client;
|
||||||
$clientSelect.append(new Option(getClientDisplayName(client), client.public_id));
|
$clientSelect.append(new Option(clientName, client.public_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@if ($data)
|
@if ($data)
|
||||||
|
@ -133,12 +133,13 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
window.onDatatableReady = function() {
|
window.onDatatableReady = function() {
|
||||||
$(':checkbox').click(function() {
|
$(':checkbox').unbind('click').click(function() {
|
||||||
setBulkActionsEnabled();
|
setBulkActionsEnabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('tbody tr').click(function(event) {
|
$('tbody tr').unbind('click').click(function(event) {
|
||||||
if (event.target.type !== 'checkbox' && event.target.type !== 'button' && event.target.tagName.toLowerCase() !== 'a') {
|
if (event.target.type !== 'checkbox' && event.target.type !== 'button' && event.target.tagName.toLowerCase() !== 'a') {
|
||||||
|
console.log('click');
|
||||||
$checkbox = $(this).closest('tr').find(':checkbox:not(:disabled)');
|
$checkbox = $(this).closest('tr').find(':checkbox:not(:disabled)');
|
||||||
var checked = $checkbox.prop('checked');
|
var checked = $checkbox.prop('checked');
|
||||||
$checkbox.prop('checked', !checked);
|
$checkbox.prop('checked', !checked);
|
||||||
|
11
resources/views/public/logo.blade.php
Normal file
11
resources/views/public/logo.blade.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
@extends('master')
|
||||||
|
|
||||||
|
@section('body')
|
||||||
|
|
||||||
|
<center style="padding-top:160px">
|
||||||
|
<a href="{{ NINJA_WEB_URL }}" target="_blank">
|
||||||
|
<img src="{{ asset('images/round_logo.png') }}"/>
|
||||||
|
</a>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
@stop
|
@ -30,7 +30,7 @@
|
|||||||
"table": {
|
"table": {
|
||||||
"body": "$invoiceDetails"
|
"body": "$invoiceDetails"
|
||||||
},
|
},
|
||||||
"margin": [0, 4, 12, 4],
|
"margin": [0, 0, 12, 4],
|
||||||
"layout": "noBorders"
|
"layout": "noBorders"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -48,8 +48,8 @@
|
|||||||
"hLineColor": "#D8D8D8",
|
"hLineColor": "#D8D8D8",
|
||||||
"paddingLeft": "$amount:8",
|
"paddingLeft": "$amount:8",
|
||||||
"paddingRight": "$amount:8",
|
"paddingRight": "$amount:8",
|
||||||
"paddingTop": "$amount:4",
|
"paddingTop": "$amount:6",
|
||||||
"paddingBottom": "$amount:4"
|
"paddingBottom": "$amount:2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
60
tests/acceptance/GoProCest.php
Normal file
60
tests/acceptance/GoProCest.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Codeception\Util\Fixtures;
|
||||||
|
use \AcceptanceTester;
|
||||||
|
use Faker\Factory;
|
||||||
|
|
||||||
|
class GoProCest
|
||||||
|
{
|
||||||
|
private $faker;
|
||||||
|
|
||||||
|
public function _before(AcceptanceTester $I)
|
||||||
|
{
|
||||||
|
$this->faker = Factory::create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function signUpAndGoPro(AcceptanceTester $I)
|
||||||
|
{
|
||||||
|
$userEmail = $this->faker->safeEmail;
|
||||||
|
$userPassword = $this->faker->password;
|
||||||
|
|
||||||
|
$I->wantTo('test purchasing a pro plan');
|
||||||
|
$I->amOnPage('/invoice_now');
|
||||||
|
|
||||||
|
$I->click('Sign Up');
|
||||||
|
$I->wait(1);
|
||||||
|
|
||||||
|
$I->fillField(['name' =>'new_first_name'], $this->faker->firstName);
|
||||||
|
$I->fillField(['name' =>'new_last_name'], $this->faker->lastName);
|
||||||
|
$I->fillField(['name' =>'new_email'], $userEmail);
|
||||||
|
$I->fillField(['name' =>'new_password'], $userPassword);
|
||||||
|
$I->checkOption('#terms_checkbox');
|
||||||
|
$I->click('Save');
|
||||||
|
$I->wait(1);
|
||||||
|
|
||||||
|
$I->amOnPage('/dashboard');
|
||||||
|
$I->click('Go Pro');
|
||||||
|
$I->wait(1);
|
||||||
|
|
||||||
|
$I->click('Upgrade Now!');
|
||||||
|
$I->wait(1);
|
||||||
|
|
||||||
|
$I->fillField(['name' => 'address1'], $this->faker->streetAddress);
|
||||||
|
$I->fillField(['name' => 'address2'], $this->faker->streetAddress);
|
||||||
|
$I->fillField(['name' => 'city'], $this->faker->city);
|
||||||
|
$I->fillField(['name' => 'state'], $this->faker->state);
|
||||||
|
$I->fillField(['name' => 'postal_code'], $this->faker->postcode);
|
||||||
|
$I->selectDropdown($I, 'United States', '.country-select .dropdown-toggle');
|
||||||
|
$I->fillField(['name' => 'card_number'], '4242424242424242');
|
||||||
|
$I->fillField(['name' => 'cvv'], '1234');
|
||||||
|
$I->selectOption('#expiration_month', 12);
|
||||||
|
$I->selectOption('#expiration_year', date('Y'));
|
||||||
|
$I->click('.btn-success');
|
||||||
|
$I->wait(1);
|
||||||
|
|
||||||
|
$I->see('Successfully applied payment');
|
||||||
|
|
||||||
|
$I->amOnPage('/dashboard');
|
||||||
|
$I->dontSee('Go Pro');
|
||||||
|
}
|
||||||
|
}
|
@ -79,5 +79,14 @@ class OnlinePaymentCest
|
|||||||
$I->click('.btn-success');
|
$I->click('.btn-success');
|
||||||
$I->see('Successfully applied payment');
|
$I->see('Successfully applied payment');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// create recurring invoice and auto-bill
|
||||||
|
$I->amOnPage('/recurring_invoices/create');
|
||||||
|
$I->selectDropdown($I, $clientEmail, '.client_select .dropdown-toggle');
|
||||||
|
$I->fillField('table.invoice-table tbody tr:nth-child(1) #product_key', $productKey);
|
||||||
|
$I->checkOption('#auto_bill');
|
||||||
|
$I->executeJS('preparePdfData(\'email\')');
|
||||||
|
$I->see("$0.00");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user