mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge branch 'release-4.4.0'
This commit is contained in:
commit
748578a68a
@ -6,8 +6,6 @@ sudo: true
|
||||
group: deprecated-2017Q4
|
||||
|
||||
php:
|
||||
# - 7.0
|
||||
- 7.1
|
||||
- 7.2
|
||||
|
||||
addons:
|
||||
@ -110,10 +108,10 @@ after_script:
|
||||
- mysql -u root -e 'select * from account_gateways;' ninja
|
||||
- mysql -u root -e 'select * from clients;' ninja
|
||||
- mysql -u root -e 'select * from contacts;' ninja
|
||||
- mysql -u root -e 'select id, account_id, invoice_number, amount, balance from invoices;' ninja
|
||||
- mysql -u root -e 'select id, public_id, account_id, invoice_number, amount, balance from invoices;' ninja
|
||||
- mysql -u root -e 'select * from invoice_items;' ninja
|
||||
- mysql -u root -e 'select * from invitations;' ninja
|
||||
- mysql -u root -e 'select id, account_id, invoice_id, amount, transaction_reference from payments;' ninja
|
||||
- mysql -u root -e 'select id, public_id, account_id, invoice_id, amount, transaction_reference from payments;' ninja
|
||||
- mysql -u root -e 'select * from credits;' ninja
|
||||
- mysql -u root -e 'select * from expenses;' ninja
|
||||
- mysql -u root -e 'select * from accounts;' ninja
|
||||
|
@ -69,10 +69,10 @@ class MakeModule extends Command
|
||||
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'transformer', '--fields' => $fields]);
|
||||
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'lang', '--filename' => 'texts']);
|
||||
|
||||
if ($migrate == 'false') {
|
||||
$this->info("Use the following command to run the migrations:\nphp artisan module:migrate $name");
|
||||
} else {
|
||||
if ($migrate == 'true') {
|
||||
Artisan::call('module:migrate', ['module' => $name]);
|
||||
} else {
|
||||
$this->info("Use the following command to run the migrations:\nphp artisan module:migrate $name");
|
||||
}
|
||||
|
||||
Artisan::call('module:dump');
|
||||
|
@ -91,7 +91,7 @@ class SendRecurringInvoices extends Command
|
||||
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS TRUE AND is_public IS TRUE AND frequency_id > 0 AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', [$today, $today])
|
||||
->orderBy('id', 'asc')
|
||||
->get();
|
||||
$this->info($invoices->count() . ' recurring invoice(s) found');
|
||||
$this->info(date('r ') . $invoices->count() . ' recurring invoice(s) found');
|
||||
|
||||
foreach ($invoices as $recurInvoice) {
|
||||
$shouldSendToday = $recurInvoice->shouldSendToday();
|
||||
@ -100,7 +100,7 @@ class SendRecurringInvoices extends Command
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->info('Processing Invoice: '. $recurInvoice->id);
|
||||
$this->info(date('r') . ' Processing Invoice: '. $recurInvoice->id);
|
||||
|
||||
$account = $recurInvoice->account;
|
||||
$account->loadLocalizationSettings($recurInvoice->client);
|
||||
@ -109,13 +109,13 @@ class SendRecurringInvoices extends Command
|
||||
try {
|
||||
$invoice = $this->invoiceRepo->createRecurringInvoice($recurInvoice);
|
||||
if ($invoice && ! $invoice->isPaid() && $account->auto_email_invoice) {
|
||||
$this->info('Not billed - Sending Invoice');
|
||||
$this->info(date('r') . ' Not billed - Sending Invoice');
|
||||
dispatch(new SendInvoiceEmail($invoice, $invoice->user_id));
|
||||
} elseif ($invoice) {
|
||||
$this->info('Successfully billed invoice');
|
||||
$this->info(date('r') . ' Successfully billed invoice');
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
$this->info('Error: ' . $exception->getMessage());
|
||||
$this->info(date('r') . ' Error: ' . $exception->getMessage());
|
||||
Utils::logError($exception);
|
||||
}
|
||||
|
||||
@ -133,7 +133,7 @@ class SendRecurringInvoices extends Command
|
||||
[$today->format('Y-m-d')])
|
||||
->orderBy('invoices.id', 'asc')
|
||||
->get();
|
||||
$this->info($delayedAutoBillInvoices->count() . ' due recurring invoice instance(s) found');
|
||||
$this->info(date('r ') . $delayedAutoBillInvoices->count() . ' due recurring invoice instance(s) found');
|
||||
|
||||
/** @var Invoice $invoice */
|
||||
foreach ($delayedAutoBillInvoices as $invoice) {
|
||||
@ -142,7 +142,7 @@ class SendRecurringInvoices extends Command
|
||||
}
|
||||
|
||||
if ($invoice->getAutoBillEnabled() && $invoice->client->autoBillLater()) {
|
||||
$this->info('Processing Autobill-delayed Invoice: ' . $invoice->id);
|
||||
$this->info(date('r') . ' Processing Autobill-delayed Invoice: ' . $invoice->id);
|
||||
Auth::loginUsingId($invoice->activeUser()->id);
|
||||
$this->paymentService->autoBillInvoice($invoice);
|
||||
Auth::logout();
|
||||
@ -158,7 +158,7 @@ class SendRecurringInvoices extends Command
|
||||
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', [$today, $today])
|
||||
->orderBy('id', 'asc')
|
||||
->get();
|
||||
$this->info($expenses->count() . ' recurring expenses(s) found');
|
||||
$this->info(date('r ') . $expenses->count() . ' recurring expenses(s) found');
|
||||
|
||||
foreach ($expenses as $expense) {
|
||||
$shouldSendToday = $expense->shouldSendToday();
|
||||
@ -167,7 +167,7 @@ class SendRecurringInvoices extends Command
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->info('Processing Expense: '. $expense->id);
|
||||
$this->info(date('r') . ' Processing Expense: '. $expense->id);
|
||||
$this->recurringExpenseRepo->createRecurringExpense($expense);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use App\Libraries\CurlUtils;
|
||||
use Carbon;
|
||||
use Str;
|
||||
use Cache;
|
||||
use Utils;
|
||||
use Exception;
|
||||
use App\Jobs\SendInvoiceEmail;
|
||||
use App\Models\Invoice;
|
||||
@ -73,7 +74,7 @@ class SendReminders extends Command
|
||||
$this->sendScheduledReports();
|
||||
$this->loadExchangeRates();
|
||||
|
||||
$this->info('Done');
|
||||
$this->info(date('r') . ' Done');
|
||||
|
||||
if ($errorEmail = env('ERROR_EMAIL')) {
|
||||
\Mail::raw('EOM', function ($message) use ($errorEmail, $database) {
|
||||
@ -87,7 +88,7 @@ class SendReminders extends Command
|
||||
private function chargeLateFees()
|
||||
{
|
||||
$accounts = $this->accountRepo->findWithFees();
|
||||
$this->info($accounts->count() . ' accounts found with fees');
|
||||
$this->info(date('r ') . $accounts->count() . ' accounts found with fees');
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
if (! $account->hasFeature(FEATURE_EMAIL_TEMPLATES_REMINDERS)) {
|
||||
@ -95,11 +96,11 @@ class SendReminders extends Command
|
||||
}
|
||||
|
||||
$invoices = $this->invoiceRepo->findNeedingReminding($account, false);
|
||||
$this->info($account->name . ': ' . $invoices->count() . ' invoices found');
|
||||
$this->info(date('r ') . $account->name . ': ' . $invoices->count() . ' invoices found');
|
||||
|
||||
foreach ($invoices as $invoice) {
|
||||
if ($reminder = $account->getInvoiceReminder($invoice, false)) {
|
||||
$this->info('Charge fee: ' . $invoice->id);
|
||||
$this->info(date('r') . ' Charge fee: ' . $invoice->id);
|
||||
$account->loadLocalizationSettings($invoice->client); // support trans to add fee line item
|
||||
$number = preg_replace('/[^0-9]/', '', $reminder);
|
||||
|
||||
@ -114,7 +115,7 @@ class SendReminders extends Command
|
||||
private function sendReminderEmails()
|
||||
{
|
||||
$accounts = $this->accountRepo->findWithReminders();
|
||||
$this->info(count($accounts) . ' accounts found with reminders');
|
||||
$this->info(date('r ') . count($accounts) . ' accounts found with reminders');
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
if (! $account->hasFeature(FEATURE_EMAIL_TEMPLATES_REMINDERS)) {
|
||||
@ -123,27 +124,27 @@ class SendReminders extends Command
|
||||
|
||||
// standard reminders
|
||||
$invoices = $this->invoiceRepo->findNeedingReminding($account);
|
||||
$this->info($account->name . ': ' . $invoices->count() . ' invoices found');
|
||||
$this->info(date('r ') . $account->name . ': ' . $invoices->count() . ' invoices found');
|
||||
|
||||
foreach ($invoices as $invoice) {
|
||||
if ($reminder = $account->getInvoiceReminder($invoice)) {
|
||||
if ($invoice->last_sent_date == date('Y-m-d')) {
|
||||
continue;
|
||||
}
|
||||
$this->info('Send email: ' . $invoice->id);
|
||||
$this->info(date('r') . ' Send email: ' . $invoice->id);
|
||||
dispatch(new SendInvoiceEmail($invoice, $invoice->user_id, $reminder));
|
||||
}
|
||||
}
|
||||
|
||||
// endless reminders
|
||||
$invoices = $this->invoiceRepo->findNeedingEndlessReminding($account);
|
||||
$this->info($account->name . ': ' . $invoices->count() . ' endless invoices found');
|
||||
$this->info(date('r ') . $account->name . ': ' . $invoices->count() . ' endless invoices found');
|
||||
|
||||
foreach ($invoices as $invoice) {
|
||||
if ($invoice->last_sent_date == date('Y-m-d')) {
|
||||
continue;
|
||||
}
|
||||
$this->info('Send email: ' . $invoice->id);
|
||||
$this->info(date('r') . ' Send email: ' . $invoice->id);
|
||||
dispatch(new SendInvoiceEmail($invoice, $invoice->user_id, 'reminder4'));
|
||||
}
|
||||
}
|
||||
@ -154,10 +155,10 @@ class SendReminders extends Command
|
||||
$scheduledReports = ScheduledReport::where('send_date', '<=', date('Y-m-d'))
|
||||
->with('user', 'account.company')
|
||||
->get();
|
||||
$this->info($scheduledReports->count() . ' scheduled reports');
|
||||
$this->info(date('r ') . $scheduledReports->count() . ' scheduled reports');
|
||||
|
||||
foreach ($scheduledReports as $scheduledReport) {
|
||||
$this->info('Processing report: ' . $scheduledReport->id);
|
||||
$this->info(date('r') . ' Processing report: ' . $scheduledReport->id);
|
||||
|
||||
$user = $scheduledReport->user;
|
||||
$account = $scheduledReport->account;
|
||||
@ -179,12 +180,12 @@ class SendReminders extends Command
|
||||
if ($file) {
|
||||
try {
|
||||
$this->userMailer->sendScheduledReport($scheduledReport, $file);
|
||||
$this->info('Sent report');
|
||||
$this->info(date('r') . ' Sent report');
|
||||
} catch (Exception $exception) {
|
||||
$this->info('ERROR: ' . $exception->getMessage());
|
||||
$this->info(date('r') . ' ERROR: ' . $exception->getMessage());
|
||||
}
|
||||
} else {
|
||||
$this->info('ERROR: Failed to run report');
|
||||
$this->info(date('r') . ' ERROR: Failed to run report');
|
||||
}
|
||||
|
||||
$scheduledReport->updateSendDate();
|
||||
@ -195,7 +196,11 @@ class SendReminders extends Command
|
||||
|
||||
private function loadExchangeRates()
|
||||
{
|
||||
$this->info('Loading latest exchange rates...');
|
||||
if (Utils::isNinjaDev()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->info(date('r') . ' Loading latest exchange rates...');
|
||||
|
||||
$data = CurlUtils::get(config('ninja.exchange_rates_url'));
|
||||
$data = json_decode($data);
|
||||
|
@ -3,7 +3,7 @@
|
||||
if (! defined('APP_NAME')) {
|
||||
define('APP_NAME', env('APP_NAME', 'Invoice Ninja'));
|
||||
define('APP_DOMAIN', env('APP_DOMAIN', 'invoiceninja.com'));
|
||||
define('CONTACT_EMAIL', env('MAIL_FROM_ADDRESS', env('MAIL_USERNAME')));
|
||||
define('CONTACT_EMAIL', env('MAIL_FROM_ADDRESS'));
|
||||
define('CONTACT_NAME', env('MAIL_FROM_NAME'));
|
||||
define('SITE_URL', env('APP_URL'));
|
||||
|
||||
@ -158,7 +158,11 @@ if (! defined('APP_NAME')) {
|
||||
define('MAX_DOCUMENT_SIZE', env('MAX_DOCUMENT_SIZE', 10000)); // KB
|
||||
define('MAX_EMAIL_DOCUMENTS_SIZE', env('MAX_EMAIL_DOCUMENTS_SIZE', 10000)); // Total KB
|
||||
define('MAX_ZIP_DOCUMENTS_SIZE', env('MAX_EMAIL_DOCUMENTS_SIZE', 30000)); // Total KB (uncompressed)
|
||||
<<<<<<< HEAD
|
||||
define('MAX_EMAILS_SENT_PER_DAY', 500);
|
||||
=======
|
||||
define('MAX_EMAILS_SENT_PER_DAY', 300);
|
||||
>>>>>>> release-4.4.0
|
||||
define('DOCUMENT_PREVIEW_SIZE', env('DOCUMENT_PREVIEW_SIZE', 300)); // pixels
|
||||
define('DEFAULT_FONT_SIZE', 9);
|
||||
define('DEFAULT_HEADER_FONT', 1); // Roboto
|
||||
@ -180,6 +184,7 @@ if (! defined('APP_NAME')) {
|
||||
define('IMPORT_INVOICEPLANE', 'InvoicePlane');
|
||||
define('IMPORT_HARVEST', 'Harvest');
|
||||
define('IMPORT_STRIPE', 'Stripe');
|
||||
define('IMPORT_PANCAKE', 'Pancake');
|
||||
|
||||
define('MAX_NUM_CLIENTS', 100);
|
||||
define('MAX_NUM_CLIENTS_PRO', 20000);
|
||||
@ -299,9 +304,11 @@ if (! defined('APP_NAME')) {
|
||||
define('GATEWAY_PAYTRACE', 56);
|
||||
define('GATEWAY_WEPAY', 60);
|
||||
define('GATEWAY_BRAINTREE', 61);
|
||||
define('GATEWAY_CUSTOM', 62);
|
||||
define('GATEWAY_CUSTOM1', 62);
|
||||
define('GATEWAY_GOCARDLESS', 64);
|
||||
define('GATEWAY_PAYMILL', 66);
|
||||
define('GATEWAY_CUSTOM2', 67);
|
||||
define('GATEWAY_CUSTOM3', 68);
|
||||
|
||||
// The customer exists, but only as a local concept
|
||||
// The remote gateway doesn't understand the concept of customers
|
||||
@ -339,7 +346,7 @@ if (! defined('APP_NAME')) {
|
||||
define('NINJA_APP_URL', env('NINJA_APP_URL', 'https://app.invoiceninja.com'));
|
||||
define('NINJA_DOCS_URL', env('NINJA_DOCS_URL', 'http://docs.invoiceninja.com/en/latest'));
|
||||
define('NINJA_DATE', '2000-01-01');
|
||||
define('NINJA_VERSION', '4.3.1' . env('NINJA_VERSION_SUFFIX'));
|
||||
define('NINJA_VERSION', '4.4.0' . env('NINJA_VERSION_SUFFIX'));
|
||||
define('NINJA_TERMS_VERSION', '');
|
||||
|
||||
define('SOCIAL_LINK_FACEBOOK', env('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'));
|
||||
@ -451,12 +458,14 @@ if (! defined('APP_NAME')) {
|
||||
define('GATEWAY_TYPE_PAYPAL', 3);
|
||||
define('GATEWAY_TYPE_BITCOIN', 4);
|
||||
define('GATEWAY_TYPE_DWOLLA', 5);
|
||||
define('GATEWAY_TYPE_CUSTOM', 6);
|
||||
define('GATEWAY_TYPE_CUSTOM1', 6);
|
||||
define('GATEWAY_TYPE_ALIPAY', 7);
|
||||
define('GATEWAY_TYPE_SOFORT', 8);
|
||||
define('GATEWAY_TYPE_SEPA', 9);
|
||||
define('GATEWAY_TYPE_GOCARDLESS', 10);
|
||||
define('GATEWAY_TYPE_APPLE_PAY', 11);
|
||||
define('GATEWAY_TYPE_CUSTOM2', 12);
|
||||
define('GATEWAY_TYPE_CUSTOM3', 13);
|
||||
define('GATEWAY_TYPE_TOKEN', 'token');
|
||||
|
||||
define('TEMPLATE_INVOICE', 'invoice');
|
||||
@ -469,6 +478,14 @@ if (! defined('APP_NAME')) {
|
||||
define('TEMPLATE_REMINDER3', 'reminder3');
|
||||
define('TEMPLATE_REMINDER4', 'reminder4');
|
||||
|
||||
define('CUSTOM_MESSAGE_DASHBOARD', 'dashboard');
|
||||
define('CUSTOM_MESSAGE_UNPAID_INVOICE', 'unpaid_invoice');
|
||||
define('CUSTOM_MESSAGE_PAID_INVOICE', 'paid_invoice');
|
||||
define('CUSTOM_MESSAGE_UNAPPROVED_QUOTE', 'unapproved_quote');
|
||||
define('CUSTOM_MESSAGE_APPROVED_QUOTE', 'approved_quote');
|
||||
define('CUSTOM_MESSAGE_UNAPPROVED_PROPOSAL', 'unapproved_proposal');
|
||||
define('CUSTOM_MESSAGE_APPROVED_PROPOSAL', 'approved_proposal');
|
||||
|
||||
define('RESET_FREQUENCY_DAILY', 1);
|
||||
define('RESET_FREQUENCY_WEEKLY', 2);
|
||||
define('RESET_FREQUENCY_MONTHLY', 3);
|
||||
|
File diff suppressed because one or more lines are too long
@ -52,7 +52,7 @@ class AccountGatewayController extends BaseController
|
||||
$accountGateway = AccountGateway::scope($publicId)->firstOrFail();
|
||||
$config = $accountGateway->getConfig();
|
||||
|
||||
if ($accountGateway->gateway_id != GATEWAY_CUSTOM) {
|
||||
if (! $accountGateway->isCustom()) {
|
||||
foreach ($config as $field => $value) {
|
||||
$config->$field = str_repeat('*', strlen($value));
|
||||
}
|
||||
@ -257,8 +257,6 @@ class AccountGatewayController extends BaseController
|
||||
}
|
||||
if (! $value && in_array($field, ['testMode', 'developerMode', 'sandbox'])) {
|
||||
// do nothing
|
||||
} elseif ($gatewayId == GATEWAY_CUSTOM) {
|
||||
$config->$field = Utils::isNinjaProd() ? strip_tags($value) : $value;
|
||||
} else {
|
||||
$config->$field = $value;
|
||||
}
|
||||
|
@ -282,7 +282,10 @@ class AppController extends BaseController
|
||||
if (! Utils::isNinjaProd()) {
|
||||
if ($password = env('UPDATE_SECRET')) {
|
||||
if (! hash_equals($password, request('secret') ?: '')) {
|
||||
abort(400, 'Invalid secret: /update?secret=<value>');
|
||||
$message = 'Invalid secret: /update?secret=<value>';
|
||||
Utils::logError($message);
|
||||
echo $message;
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,7 +331,7 @@ class AppController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
return Redirect::to('/');
|
||||
return Redirect::to('/?clear_cache=true');
|
||||
}
|
||||
|
||||
// MySQL changed the default table type from MyISAM to InnoDB
|
||||
|
@ -58,8 +58,9 @@ class ResetPasswordController extends Controller
|
||||
|
||||
public function showResetForm(Request $request, $token = null)
|
||||
{
|
||||
return view('auth.passwords.reset')->with(
|
||||
['token' => $token]
|
||||
);
|
||||
return view('auth.passwords.reset')->with([
|
||||
'token' => $token,
|
||||
'url' => '/password/reset'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -158,8 +158,8 @@ class BotController extends Controller
|
||||
|
||||
private function parseMessage($message)
|
||||
{
|
||||
$appId = env('MSBOT_LUIS_APP_ID');
|
||||
$subKey = env('MSBOT_LUIS_SUBSCRIPTION_KEY');
|
||||
$appId = config('ninja.voice_commands.app_id');
|
||||
$subKey = config('ninja.voice_commands.subscription_key');
|
||||
$message = rawurlencode($message);
|
||||
|
||||
$url = sprintf('%s/%s?subscription-key=%s&verbose=true&q=%s', MSBOT_LUIS_URL, $appId, $subKey, $message);
|
||||
|
@ -54,9 +54,10 @@ class ResetPasswordController extends Controller
|
||||
|
||||
public function showResetForm(Request $request, $token = null)
|
||||
{
|
||||
return view('clientauth.passwords.reset')->with(
|
||||
['token' => $token]
|
||||
);
|
||||
return view('auth.passwords.reset')->with([
|
||||
'token' => $token,
|
||||
'url' => '/client/password/reset'
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -197,8 +197,8 @@ class ClientController extends BaseController
|
||||
'data' => Input::old('data'),
|
||||
'account' => Auth::user()->account,
|
||||
'sizes' => Cache::get('sizes'),
|
||||
'customLabel1' => Auth::user()->account->custom_client_label1,
|
||||
'customLabel2' => Auth::user()->account->custom_client_label2,
|
||||
'customLabel1' => Auth::user()->account->customLabel('client1'),
|
||||
'customLabel2' => Auth::user()->account->customLabel('client2'),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -128,7 +128,7 @@ class ClientPortalController extends BaseController
|
||||
$paymentURL = '';
|
||||
if (count($paymentTypes) == 1) {
|
||||
$paymentURL = $paymentTypes[0]['url'];
|
||||
if ($paymentTypes[0]['gatewayTypeId'] == GATEWAY_TYPE_CUSTOM) {
|
||||
if (in_array($paymentTypes[0]['gatewayTypeId'], [GATEWAY_TYPE_CUSTOM1, GATEWAY_TYPE_CUSTOM2, GATEWAY_TYPE_CUSTOM3])) {
|
||||
// do nothing
|
||||
} elseif (! $account->isGatewayConfigured(GATEWAY_PAYPAL_EXPRESS)) {
|
||||
$paymentURL = URL::to($paymentURL);
|
||||
@ -170,13 +170,6 @@ class ClientPortalController extends BaseController
|
||||
'accountGateway' => $paymentDriver->accountGateway,
|
||||
];
|
||||
}
|
||||
|
||||
if ($accountGateway = $account->getGatewayByType(GATEWAY_TYPE_CUSTOM)) {
|
||||
$data += [
|
||||
'customGatewayName' => $accountGateway->getConfigField('name'),
|
||||
'customGatewayText' => $accountGateway->getConfigField('text'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($account->hasFeature(FEATURE_DOCUMENTS) && $this->canCreateZip()) {
|
||||
@ -215,7 +208,7 @@ class ClientPortalController extends BaseController
|
||||
|
||||
$invoice = $invitation->invoice;
|
||||
$decode = ! request()->base64;
|
||||
$pdfString = $invoice->getPDFString($decode);
|
||||
$pdfString = $invoice->getPDFString($invitation, $decode);
|
||||
|
||||
header('Content-Type: application/pdf');
|
||||
header('Content-Length: ' . strlen($pdfString));
|
||||
|
@ -21,6 +21,7 @@ class DashboardApiController extends BaseAPIController
|
||||
$viewAll = $user->hasPermission('view_all');
|
||||
$userId = $user->id;
|
||||
$accountId = $user->account->id;
|
||||
$defaultCurrency = $user->account->currency_id;
|
||||
|
||||
$dashboardRepo = $this->dashboardRepo;
|
||||
$metrics = $dashboardRepo->totals($accountId, $userId, $viewAll);
|
||||
@ -44,11 +45,11 @@ class DashboardApiController extends BaseAPIController
|
||||
$data = [
|
||||
'id' => 1,
|
||||
'paidToDate' => $paidToDate->count() && $paidToDate[0]->value ? $paidToDate[0]->value : 0,
|
||||
'paidToDateCurrency' => $paidToDate->count() && $paidToDate[0]->currency_id ? $paidToDate[0]->currency_id : 0,
|
||||
'paidToDateCurrency' => $paidToDate->count() && $paidToDate[0]->currency_id ? $paidToDate[0]->currency_id : $defaultCurrency,
|
||||
'balances' => $balances->count() && $balances[0]->value ? $balances[0]->value : 0,
|
||||
'balancesCurrency' => $balances->count() && $balances[0]->currency_id ? $balances[0]->currency_id : 0,
|
||||
'balancesCurrency' => $balances->count() && $balances[0]->currency_id ? $balances[0]->currency_id : $defaultCurrency,
|
||||
'averageInvoice' => $averageInvoice->count() && $averageInvoice[0]->invoice_avg ? $averageInvoice[0]->invoice_avg : 0,
|
||||
'averageInvoiceCurrency' => $averageInvoice->count() && $averageInvoice[0]->currency_id ? $averageInvoice[0]->currency_id : 0,
|
||||
'averageInvoiceCurrency' => $averageInvoice->count() && $averageInvoice[0]->currency_id ? $averageInvoice[0]->currency_id : $defaultCurrency,
|
||||
'invoicesSent' => $metrics ? $metrics->invoices_sent : 0,
|
||||
'activeClients' => $metrics ? $metrics->active_clients : 0,
|
||||
'activities' => $this->createCollection($activities, new ActivityTransformer(), ENTITY_ACTIVITY),
|
||||
|
@ -149,17 +149,7 @@ class HomeController extends BaseController
|
||||
$subject = 'Customer Message [';
|
||||
if (Utils::isNinjaProd()) {
|
||||
$subject .= str_replace('db-ninja-', '', config('database.default'));
|
||||
$account = Auth::user()->account;
|
||||
if ($account->isTrial()) {
|
||||
$subject .= 'T';
|
||||
} elseif ($account->isEnterprise()) {
|
||||
$subject .= 'E';
|
||||
} elseif ($account->isPro()) {
|
||||
$subject .= 'P';
|
||||
} else {
|
||||
$subject .= 'H';
|
||||
}
|
||||
$subject .= '] ';
|
||||
$subject .= Auth::user()->present()->statusCode . '] ';
|
||||
} else {
|
||||
$subject .= 'Self-Host] | ';
|
||||
}
|
||||
|
@ -244,6 +244,16 @@ class NinjaController extends BaseController
|
||||
$licenseKey = Input::get('license_key');
|
||||
$productId = Input::get('product_id', PRODUCT_ONE_CLICK_INSTALL);
|
||||
|
||||
// add in dashes
|
||||
if (strlen($licenseKey) == 20) {
|
||||
$licenseKey = sprintf('%s-%s-%s-%s-%s',
|
||||
substr($licenseKey, 0, 4),
|
||||
substr($licenseKey, 4, 4),
|
||||
substr($licenseKey, 8, 4),
|
||||
substr($licenseKey, 12, 4),
|
||||
substr($licenseKey, 16, 4));
|
||||
}
|
||||
|
||||
$license = License::where('license_key', '=', $licenseKey)
|
||||
->where('is_claimed', '<', 10)
|
||||
->where('product_id', '=', $productId)
|
||||
|
@ -162,6 +162,7 @@ class ReportController extends BaseController
|
||||
$schedule->config = json_encode($options);
|
||||
$schedule->frequency = request('frequency');
|
||||
$schedule->send_date = Utils::toSqlDate(request('send_date'));
|
||||
$schedule->ip = request()->getClientIp();
|
||||
$schedule->save();
|
||||
|
||||
session()->flash('message', trans('texts.created_scheduled_report'));
|
||||
|
@ -149,6 +149,9 @@ class TaskApiController extends BaseAPIController
|
||||
*/
|
||||
public function update(UpdateTaskRequest $request)
|
||||
{
|
||||
if ($request->action) {
|
||||
return $this->handleAction($request);
|
||||
}
|
||||
|
||||
$task = $request->entity();
|
||||
|
||||
|
@ -8,6 +8,7 @@ use App\Http\Requests\UpdateTaskRequest;
|
||||
use App\Models\Client;
|
||||
use App\Models\Project;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaskStatus;
|
||||
use App\Ninja\Datatables\TaskDatatable;
|
||||
use App\Ninja\Repositories\InvoiceRepository;
|
||||
use App\Ninja\Repositories\TaskRepository;
|
||||
@ -267,6 +268,14 @@ class TaskController extends BaseController
|
||||
$this->taskRepo->save($ids, ['action' => $action]);
|
||||
Session::flash('message', trans($action == 'stop' ? 'texts.stopped_task' : 'texts.resumed_task'));
|
||||
return $this->returnBulk($this->entityType, $action, $ids);
|
||||
} elseif (strpos($action, 'update_status') === 0) {
|
||||
list($action, $statusPublicId) = explode(':', $action);
|
||||
Task::scope($ids)->update([
|
||||
'task_status_id' => TaskStatus::getPrivateId($statusPublicId),
|
||||
'task_status_sort_order' => 9999,
|
||||
]);
|
||||
Session::flash('message', trans('texts.updated_task_status'));
|
||||
return $this->returnBulk($this->entityType, $action, $ids);
|
||||
} elseif ($action == 'invoice' || $action == 'add_to_invoice') {
|
||||
$tasks = Task::scope($ids)->with('account', 'client', 'project')->orderBy('project_id', 'id')->get();
|
||||
$clientPublicId = false;
|
||||
|
@ -103,7 +103,7 @@ class Authenticate
|
||||
} else {
|
||||
if ($guard == 'client') {
|
||||
$url = '/client/login';
|
||||
if (Utils::isNinja()) {
|
||||
if (Utils::isNinjaProd()) {
|
||||
if ($account && Utils::getSubdomain() == 'app') {
|
||||
$url .= '?account_key=' . $account->account_key;
|
||||
}
|
||||
|
@ -431,14 +431,12 @@ class Utils
|
||||
|
||||
public static function prepareErrorData($context)
|
||||
{
|
||||
return [
|
||||
$data = [
|
||||
'context' => $context,
|
||||
'user_id' => Auth::check() ? Auth::user()->id : 0,
|
||||
'account_id' => Auth::check() ? Auth::user()->account_id : 0,
|
||||
'user_name' => Auth::check() ? Auth::user()->getDisplayName() : '',
|
||||
'method' => Request::method(),
|
||||
'url' => Input::get('url', Request::url()),
|
||||
'previous' => url()->previous(),
|
||||
'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '',
|
||||
'locale' => App::getLocale(),
|
||||
'ip' => Request::getClientIp(),
|
||||
@ -447,6 +445,15 @@ class Utils
|
||||
'is_api' => session('token_id') ? 'yes' : 'no',
|
||||
'db_server' => config('database.default'),
|
||||
];
|
||||
|
||||
if (static::isNinja()) {
|
||||
$data['url'] = Input::get('url', Request::url());
|
||||
$data['previous'] = url()->previous();
|
||||
} else {
|
||||
$data['url'] = request()->path();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public static function getErrors()
|
||||
@ -489,6 +496,21 @@ class Utils
|
||||
return intval($value);
|
||||
}
|
||||
|
||||
public static function lookupIdInCache($name, $type)
|
||||
{
|
||||
$cache = Cache::get($type);
|
||||
|
||||
$data = $cache->filter(function ($item) use ($name) {
|
||||
return strtolower($item->name) == trim(strtolower($name));
|
||||
});
|
||||
|
||||
if ($record = $data->first()) {
|
||||
return $record->id;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getFromCache($id, $type)
|
||||
{
|
||||
$cache = Cache::get($type);
|
||||
|
@ -79,7 +79,7 @@ class HandleUserLoggedIn
|
||||
|
||||
if (! Utils::isNinja()) {
|
||||
// check custom gateway id is correct
|
||||
$gateway = Gateway::find(GATEWAY_CUSTOM);
|
||||
$gateway = Gateway::find(GATEWAY_CUSTOM1);
|
||||
if (! $gateway || $gateway->name !== 'Custom') {
|
||||
Session::flash('error', trans('texts.error_incorrect_gateway_ids'));
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ use App\Models\Traits\GeneratesNumbers;
|
||||
use App\Models\Traits\PresentsInvoice;
|
||||
use App\Models\Traits\SendsEmails;
|
||||
use App\Models\Traits\HasLogo;
|
||||
use App\Models\Traits\HasCustomMessages;
|
||||
use Cache;
|
||||
use Carbon;
|
||||
use DateTime;
|
||||
@ -30,6 +31,7 @@ class Account extends Eloquent
|
||||
use GeneratesNumbers;
|
||||
use SendsEmails;
|
||||
use HasLogo;
|
||||
use HasCustomMessages;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
@ -72,22 +74,12 @@ class Account extends Eloquent
|
||||
'work_phone',
|
||||
'work_email',
|
||||
'language_id',
|
||||
'custom_label1',
|
||||
'custom_value1',
|
||||
'custom_label2',
|
||||
'custom_value2',
|
||||
'custom_client_label1',
|
||||
'custom_client_label2',
|
||||
'fill_products',
|
||||
'update_products',
|
||||
'primary_color',
|
||||
'secondary_color',
|
||||
'hide_quantity',
|
||||
'hide_paid_to_date',
|
||||
'custom_invoice_label1',
|
||||
'custom_invoice_label2',
|
||||
'custom_invoice_taxes1',
|
||||
'custom_invoice_taxes2',
|
||||
'vat_number',
|
||||
'invoice_number_prefix',
|
||||
'invoice_number_counter',
|
||||
@ -112,8 +104,6 @@ class Account extends Eloquent
|
||||
'num_days_reminder1',
|
||||
'num_days_reminder2',
|
||||
'num_days_reminder3',
|
||||
'custom_invoice_text_label1',
|
||||
'custom_invoice_text_label2',
|
||||
'tax_name1',
|
||||
'tax_rate1',
|
||||
'tax_name2',
|
||||
@ -142,8 +132,6 @@ class Account extends Eloquent
|
||||
'show_currency_code',
|
||||
'enable_portal_password',
|
||||
'send_portal_password',
|
||||
'custom_invoice_item_label1',
|
||||
'custom_invoice_item_label2',
|
||||
'recurring_invoice_number_prefix',
|
||||
'enable_client_portal',
|
||||
'invoice_fields',
|
||||
@ -175,8 +163,6 @@ class Account extends Eloquent
|
||||
'gateway_fee_enabled',
|
||||
'send_item_details',
|
||||
'reset_counter_date',
|
||||
'custom_contact_label1',
|
||||
'custom_contact_label2',
|
||||
'domain_id',
|
||||
'analytics_key',
|
||||
'credit_number_counter',
|
||||
@ -186,6 +172,10 @@ class Account extends Eloquent
|
||||
'inclusive_taxes',
|
||||
'convert_products',
|
||||
'signature_on_pdf',
|
||||
'custom_fields',
|
||||
'custom_value1',
|
||||
'custom_value2',
|
||||
'custom_messages',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -233,6 +223,27 @@ class Account extends Eloquent
|
||||
'outstanding' => 4,
|
||||
];
|
||||
|
||||
public static $customFields = [
|
||||
'client1',
|
||||
'client2',
|
||||
'contact1',
|
||||
'contact2',
|
||||
'product1',
|
||||
'product2',
|
||||
'invoice1',
|
||||
'invoice2',
|
||||
'invoice_surcharge1',
|
||||
'invoice_surcharge2',
|
||||
'task1',
|
||||
'task2',
|
||||
'project1',
|
||||
'project2',
|
||||
'expense1',
|
||||
'expense2',
|
||||
'vendor1',
|
||||
'vendor2',
|
||||
];
|
||||
|
||||
public static $customLabels = [
|
||||
'balance_due',
|
||||
'credit_card',
|
||||
@ -240,6 +251,8 @@ class Account extends Eloquent
|
||||
'description',
|
||||
'discount',
|
||||
'due_date',
|
||||
'gateway_fee_item',
|
||||
'gateway_fee_description',
|
||||
'hours',
|
||||
'id_number',
|
||||
'invoice',
|
||||
@ -265,6 +278,16 @@ class Account extends Eloquent
|
||||
'vat_number',
|
||||
];
|
||||
|
||||
public static $customMessageTypes = [
|
||||
CUSTOM_MESSAGE_DASHBOARD,
|
||||
CUSTOM_MESSAGE_UNPAID_INVOICE,
|
||||
CUSTOM_MESSAGE_PAID_INVOICE,
|
||||
CUSTOM_MESSAGE_UNAPPROVED_QUOTE,
|
||||
//CUSTOM_MESSAGE_APPROVED_QUOTE,
|
||||
//CUSTOM_MESSAGE_UNAPPROVED_PROPOSAL,
|
||||
//CUSTOM_MESSAGE_APPROVED_PROPOSAL,
|
||||
];
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
@ -361,6 +384,11 @@ class Account extends Eloquent
|
||||
return $this->hasMany('App\Models\Document')->whereIsDefault(true);
|
||||
}
|
||||
|
||||
public function background_image()
|
||||
{
|
||||
return $this->hasOne('App\Models\Document', 'id', 'background_image_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
@ -497,6 +525,38 @@ class Account extends Eloquent
|
||||
$this->attributes['size_id'] = $value ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
*/
|
||||
public function setCustomFieldsAttribute($data)
|
||||
{
|
||||
$fields = [];
|
||||
|
||||
if (! is_array($data)) {
|
||||
$data = json_decode($data);
|
||||
}
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if ($value) {
|
||||
$fields[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$this->attributes['custom_fields'] = count($fields) ? json_encode($fields) : null;
|
||||
}
|
||||
|
||||
public function getCustomFieldsAttribute($value)
|
||||
{
|
||||
return json_decode($value ?: '{}');
|
||||
}
|
||||
|
||||
public function customLabel($field)
|
||||
{
|
||||
$labels = $this->custom_fields;
|
||||
|
||||
return ! empty($labels->$field) ? $labels->$field : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $gatewayId
|
||||
*
|
||||
@ -707,6 +767,14 @@ class Account extends Eloquent
|
||||
return $this->currency_id ?: DEFAULT_CURRENCY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCountryId()
|
||||
{
|
||||
return $this->country_id ?: DEFAULT_COUNTRY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $date
|
||||
*
|
||||
@ -825,8 +893,10 @@ class Account extends Eloquent
|
||||
|
||||
$gatewayTypes = [];
|
||||
$gatewayIds = [];
|
||||
$usedGatewayIds = [];
|
||||
|
||||
foreach ($this->account_gateways as $accountGateway) {
|
||||
$usedGatewayIds[] = $accountGateway->gateway_id;
|
||||
$paymentDriver = $accountGateway->paymentDriver();
|
||||
$gatewayTypes = array_unique(array_merge($gatewayTypes, $paymentDriver->gatewayTypes()));
|
||||
}
|
||||
@ -1476,28 +1546,6 @@ class Account extends Eloquent
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $field
|
||||
* @param bool $entity
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function showCustomField($field, $entity = false)
|
||||
{
|
||||
if ($this->hasFeature(FEATURE_INVOICE_SETTINGS) && $this->$field) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! $entity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// convert (for example) 'custom_invoice_label1' to 'invoice.custom_value1'
|
||||
$field = str_replace(['invoice_', 'label'], ['', 'value'], $field);
|
||||
|
||||
return Utils::isEmpty($entity->$field) ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Utils;
|
||||
use HTMLUtils;
|
||||
use Crypt;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
@ -114,6 +116,11 @@ class AccountGateway extends EntityModel
|
||||
}
|
||||
}
|
||||
|
||||
public function isCustom()
|
||||
{
|
||||
return in_array($this->gateway_id, [GATEWAY_CUSTOM1, GATEWAY_CUSTOM2, GATEWAY_CUSTOM3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $config
|
||||
*/
|
||||
@ -293,4 +300,22 @@ class AccountGateway extends EntityModel
|
||||
return $this->getConfigField('testMode');
|
||||
}
|
||||
}
|
||||
|
||||
public function getCustomHtml($invitation)
|
||||
{
|
||||
$text = $this->getConfigField('text');
|
||||
|
||||
if ($text == strip_tags($text)) {
|
||||
$text = nl2br($text);
|
||||
}
|
||||
|
||||
if (Utils::isNinja()) {
|
||||
$text = HTMLUtils::sanitizeHTML($text);
|
||||
}
|
||||
|
||||
$templateService = app('App\Services\TemplateService');
|
||||
$text = $templateService->processVariables($text, ['invitation' => $invitation]);
|
||||
|
||||
return $text;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use Carbon;
|
||||
use DB;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
use App\Models\Traits\HasCustomMessages;
|
||||
use Utils;
|
||||
|
||||
/**
|
||||
@ -15,6 +16,7 @@ class Client extends EntityModel
|
||||
{
|
||||
use PresentableTrait;
|
||||
use SoftDeletes;
|
||||
use HasCustomMessages;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
@ -61,6 +63,7 @@ class Client extends EntityModel
|
||||
'shipping_country_id',
|
||||
'show_tasks_in_portal',
|
||||
'send_reminders',
|
||||
'custom_messages',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -234,6 +234,23 @@ class Document extends EntityModel
|
||||
return $disk->get($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRawCached()
|
||||
{
|
||||
$key = 'image:' . $this->path;
|
||||
|
||||
if ($image = cache($key)) {
|
||||
// do nothing
|
||||
} else {
|
||||
$image = $this->getRaw();
|
||||
cache([$key => $image], 120);
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
@ -356,6 +373,11 @@ class Document extends EntityModel
|
||||
|
||||
return $document;
|
||||
}
|
||||
|
||||
public function scopeProposalImages($query)
|
||||
{
|
||||
return $query->whereIsProposal(1);
|
||||
}
|
||||
}
|
||||
|
||||
Document::deleted(function ($document) {
|
||||
|
@ -108,7 +108,11 @@ class EntityModel extends Eloquent
|
||||
|
||||
$className = get_called_class();
|
||||
|
||||
return $className::scope($publicId)->withTrashed()->value('id');
|
||||
if (method_exists($className, 'trashed')) {
|
||||
return $className::scope($publicId)->withTrashed()->value('id');
|
||||
} else {
|
||||
return $className::scope($publicId)->value('id');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,6 +52,8 @@ class Expense extends EntityModel
|
||||
'transaction_reference',
|
||||
'invoice_documents',
|
||||
'should_be_invoiced',
|
||||
'custom_value1',
|
||||
'custom_value2',
|
||||
];
|
||||
|
||||
public static function getImportColumns()
|
||||
@ -64,6 +66,9 @@ class Expense extends EntityModel
|
||||
'private_notes',
|
||||
'expense_category',
|
||||
'expense_date',
|
||||
'payment_type',
|
||||
'payment_date',
|
||||
'transaction_reference',
|
||||
];
|
||||
}
|
||||
|
||||
@ -76,7 +81,10 @@ class Expense extends EntityModel
|
||||
'vendor' => 'vendor',
|
||||
'notes|details^private' => 'public_notes',
|
||||
'notes|details^public' => 'private_notes',
|
||||
'date' => 'expense_date',
|
||||
'date^payment' => 'expense_date',
|
||||
'payment type' => 'payment_type',
|
||||
'payment date' => 'payment_date',
|
||||
'reference' => 'transaction_reference',
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,9 @@ class Gateway extends Eloquent
|
||||
GATEWAY_AUTHORIZE_NET,
|
||||
GATEWAY_MOLLIE,
|
||||
GATEWAY_GOCARDLESS,
|
||||
GATEWAY_CUSTOM,
|
||||
GATEWAY_CUSTOM1,
|
||||
GATEWAY_CUSTOM2,
|
||||
GATEWAY_CUSTOM3,
|
||||
];
|
||||
|
||||
// allow adding these gateway if another gateway
|
||||
@ -61,7 +63,9 @@ class Gateway extends Eloquent
|
||||
GATEWAY_GOCARDLESS,
|
||||
GATEWAY_BITPAY,
|
||||
GATEWAY_DWOLLA,
|
||||
GATEWAY_CUSTOM,
|
||||
GATEWAY_CUSTOM1,
|
||||
GATEWAY_CUSTOM2,
|
||||
GATEWAY_CUSTOM3,
|
||||
];
|
||||
|
||||
/**
|
||||
@ -141,6 +145,10 @@ class Gateway extends Eloquent
|
||||
$query->where('payment_library_id', '=', 1)
|
||||
->whereIn('id', static::$preferred)
|
||||
->whereIn('id', $accountGatewaysIds);
|
||||
|
||||
if (! Utils::isNinja()) {
|
||||
$query->where('id', '!=', GATEWAY_WEPAY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -205,6 +213,6 @@ class Gateway extends Eloquent
|
||||
|
||||
public function isCustom()
|
||||
{
|
||||
return $this->id === GATEWAY_CUSTOM;
|
||||
return in_array($this->id, [GATEWAY_CUSTOM1, GATEWAY_CUSTOM2, GATEWAY_CUSTOM3]);
|
||||
}
|
||||
}
|
||||
|
@ -529,6 +529,18 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
return $this->isType(INVOICE_TYPE_QUOTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCustomMessageType()
|
||||
{
|
||||
if ($this->isQuote()) {
|
||||
return $this->quote_invoice_id ? CUSTOM_MESSAGE_APPROVED_QUOTE : CUSTOM_MESSAGE_UNAPPROVED_QUOTE;
|
||||
} else {
|
||||
return $this->balance > 0 ? CUSTOM_MESSAGE_UNPAID_INVOICE : CUSTOM_MESSAGE_PAID_INVOICE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
@ -1001,28 +1013,17 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
'country',
|
||||
'country_id',
|
||||
'currency_id',
|
||||
'custom_label1',
|
||||
'custom_fields',
|
||||
'custom_value1',
|
||||
'custom_label2',
|
||||
'custom_value2',
|
||||
'custom_client_label1',
|
||||
'custom_client_label2',
|
||||
'custom_contact_label1',
|
||||
'custom_contact_label2',
|
||||
'primary_color',
|
||||
'secondary_color',
|
||||
'hide_quantity',
|
||||
'hide_paid_to_date',
|
||||
'all_pages_header',
|
||||
'all_pages_footer',
|
||||
'custom_invoice_label1',
|
||||
'custom_invoice_label2',
|
||||
'pdf_email_attachment',
|
||||
'show_item_taxes',
|
||||
'custom_invoice_text_label1',
|
||||
'custom_invoice_text_label2',
|
||||
'custom_invoice_item_label1',
|
||||
'custom_invoice_item_label2',
|
||||
'invoice_embed_documents',
|
||||
'page_size',
|
||||
'include_item_taxes_inline',
|
||||
@ -1224,7 +1225,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
/**
|
||||
* @return bool|string
|
||||
*/
|
||||
public function getPDFString($decode = true)
|
||||
public function getPDFString($invitation = false, $decode = true)
|
||||
{
|
||||
if (! env('PHANTOMJS_CLOUD_KEY') && ! env('PHANTOMJS_BIN_PATH')) {
|
||||
return false;
|
||||
@ -1234,7 +1235,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
return false;
|
||||
}
|
||||
|
||||
$invitation = $this->invitations[0];
|
||||
$invitation = $invitation ?: $this->invitations[0];
|
||||
$link = $invitation->getLink('view', true, true);
|
||||
$pdfString = false;
|
||||
$phantomjsSecret = env('PHANTOMJS_SECRET');
|
||||
|
@ -75,7 +75,7 @@ class InvoiceItem extends EntityModel
|
||||
return $this->belongsTo('App\Models\Account');
|
||||
}
|
||||
|
||||
public function amount()
|
||||
public function getPreTaxAmount()
|
||||
{
|
||||
$amount = $this->cost * $this->qty;
|
||||
|
||||
@ -83,21 +83,32 @@ class InvoiceItem extends EntityModel
|
||||
if ($this->invoice->is_amount_discount) {
|
||||
$amount -= $this->discount;
|
||||
} else {
|
||||
$amount -= $amount * $this->discount / 100;
|
||||
$amount -= round($amount * $this->discount / 100, 4);
|
||||
}
|
||||
}
|
||||
|
||||
$preTaxAmount = $amount;
|
||||
return $amount;
|
||||
}
|
||||
|
||||
public function getTaxAmount()
|
||||
{
|
||||
$tax = 0;
|
||||
$preTaxAmount = $this->getPreTaxAmount();
|
||||
|
||||
if ($this->tax_rate1) {
|
||||
$amount += $preTaxAmount * $this->tax_rate1 / 100;
|
||||
$tax += round($preTaxAmount * $this->tax_rate1 / 100, 2);
|
||||
}
|
||||
|
||||
if ($this->tax_rate2) {
|
||||
$amount += $preTaxAmount * $this->tax_rate2 / 100;
|
||||
$tax += round($preTaxAmount * $this->tax_rate2 / 100, 2);
|
||||
}
|
||||
|
||||
return $amount;
|
||||
return $tax;
|
||||
}
|
||||
|
||||
public function amount()
|
||||
{
|
||||
return $this->getPreTaxAmount() + $this->getTaxAmount();
|
||||
}
|
||||
|
||||
public function markFeePaid()
|
||||
|
@ -28,6 +28,8 @@ class Project extends EntityModel
|
||||
'private_notes',
|
||||
'due_date',
|
||||
'budgeted_hours',
|
||||
'custom_value1',
|
||||
'custom_value2',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -115,6 +115,19 @@ class Proposal extends EntityModel
|
||||
|
||||
return trans('texts.proposal') . '_' . $this->invoice->invoice_number . '.' . $extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCustomMessageType()
|
||||
{
|
||||
if ($this->invoice->quote_invoice_id) {
|
||||
return CUSTOM_MESSAGE_APPROVED_PROPOSAL;
|
||||
} else {
|
||||
return CUSTOM_MESSAGE_UNAPPROVED_PROPOSAL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Proposal::creating(function ($project) {
|
||||
|
@ -24,6 +24,8 @@ class Task extends EntityModel
|
||||
'description',
|
||||
'time_log',
|
||||
'is_running',
|
||||
'custom_value1',
|
||||
'custom_value2',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -259,10 +261,7 @@ class Task extends EntityModel
|
||||
$statuses[$status->public_id] = $status->name;
|
||||
}
|
||||
|
||||
if (! $taskStatues->count()) {
|
||||
$statuses[TASK_STATUS_LOGGED] = trans('texts.logged');
|
||||
}
|
||||
|
||||
$statuses[TASK_STATUS_LOGGED] = trans('texts.logged');
|
||||
$statuses[TASK_STATUS_RUNNING] = trans('texts.running');
|
||||
$statuses[TASK_STATUS_INVOICED] = trans('texts.invoiced');
|
||||
$statuses[TASK_STATUS_PAID] = trans('texts.paid');
|
||||
|
49
app/Models/Traits/HasCustomMessages.php
Normal file
49
app/Models/Traits/HasCustomMessages.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Traits;
|
||||
|
||||
/**
|
||||
* Class HasCustomMessages.
|
||||
*/
|
||||
trait HasCustomMessages
|
||||
{
|
||||
/**
|
||||
* @param $value
|
||||
*/
|
||||
public function setCustomMessagesAttribute($data)
|
||||
{
|
||||
$fields = [];
|
||||
|
||||
if (! is_array($data)) {
|
||||
$data = json_decode($data);
|
||||
}
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if ($value) {
|
||||
$fields[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$this->attributes['custom_messages'] = count($fields) ? json_encode($fields) : null;
|
||||
}
|
||||
|
||||
public function getCustomMessagesAttribute($value)
|
||||
{
|
||||
return json_decode($value ?: '{}');
|
||||
}
|
||||
|
||||
public function customMessage($type)
|
||||
{
|
||||
$messages = $this->custom_messages;
|
||||
|
||||
if (! empty($messages->$type)) {
|
||||
return $messages->$type;
|
||||
}
|
||||
|
||||
if ($this->account) {
|
||||
return $this->account->customMessage($type);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
@ -15,6 +15,18 @@ trait HasRecurrence
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldSendToday()
|
||||
{
|
||||
if (Utils::isSelfHost()) {
|
||||
return $this->shouldSendTodayNew();
|
||||
} else {
|
||||
return $this->shouldSendTodayOld();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldSendTodayOld()
|
||||
{
|
||||
if (! $this->user->confirmed) {
|
||||
return false;
|
||||
@ -79,8 +91,7 @@ trait HasRecurrence
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
public function shouldSendToday()
|
||||
public function shouldSendTodayNew()
|
||||
{
|
||||
if (! $this->user->confirmed) {
|
||||
return false;
|
||||
@ -114,7 +125,6 @@ trait HasRecurrence
|
||||
return $this->account->getDateTime() >= $nextSendDate;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @throws \Recurr\Exception\MissingData
|
||||
|
@ -101,22 +101,22 @@ trait PresentsInvoice
|
||||
]
|
||||
];
|
||||
|
||||
if ($this->custom_invoice_text_label1) {
|
||||
if ($this->customLabel('invoice_text1')) {
|
||||
$fields[INVOICE_FIELDS_INVOICE][] = 'invoice.custom_text_value1';
|
||||
}
|
||||
if ($this->custom_invoice_text_label2) {
|
||||
if ($this->customLabel('invoice_text2')) {
|
||||
$fields[INVOICE_FIELDS_INVOICE][] = 'invoice.custom_text_value2';
|
||||
}
|
||||
if ($this->custom_client_label1) {
|
||||
if ($this->customLabel('client1')) {
|
||||
$fields[INVOICE_FIELDS_CLIENT][] = 'client.custom_value1';
|
||||
}
|
||||
if ($this->custom_client_label2) {
|
||||
if ($this->customLabel('client2')) {
|
||||
$fields[INVOICE_FIELDS_CLIENT][] = 'client.custom_value2';
|
||||
}
|
||||
if ($this->custom_contact_label1) {
|
||||
if ($this->customLabel('contact1')) {
|
||||
$fields[INVOICE_FIELDS_CLIENT][] = 'contact.custom_value1';
|
||||
}
|
||||
if ($this->custom_contact_label2) {
|
||||
if ($this->customLabel('contact2')) {
|
||||
$fields[INVOICE_FIELDS_CLIENT][] = 'contact.custom_value2';
|
||||
}
|
||||
if ($this->custom_label1) {
|
||||
@ -354,18 +354,18 @@ trait PresentsInvoice
|
||||
}
|
||||
|
||||
foreach ([
|
||||
'account.custom_value1' => 'custom_label1',
|
||||
'account.custom_value2' => 'custom_label2',
|
||||
'invoice.custom_text_value1' => 'custom_invoice_text_label1',
|
||||
'invoice.custom_text_value2' => 'custom_invoice_text_label2',
|
||||
'client.custom_value1' => 'custom_client_label1',
|
||||
'client.custom_value2' => 'custom_client_label2',
|
||||
'contact.custom_value1' => 'custom_contact_label1',
|
||||
'contact.custom_value2' => 'custom_contact_label2',
|
||||
'product.custom_value1' => 'custom_invoice_item_label1',
|
||||
'product.custom_value2' => 'custom_invoice_item_label2',
|
||||
'account.custom_value1' => 'account1',
|
||||
'account.custom_value2' => 'account2',
|
||||
'invoice.custom_text_value1' => 'invoice_text1',
|
||||
'invoice.custom_text_value2' => 'invoice_text2',
|
||||
'client.custom_value1' => 'client1',
|
||||
'client.custom_value2' => 'client2',
|
||||
'contact.custom_value1' => 'contact1',
|
||||
'contact.custom_value2' => 'contact2',
|
||||
'product.custom_value1' => 'product1',
|
||||
'product.custom_value2' => 'product2',
|
||||
] as $field => $property) {
|
||||
$data[$field] = e(Utils::getCustomLabel($this->$property)) ?: trans('texts.custom_field');
|
||||
$data[$field] = e($this->present()->customLabel($property)) ?: trans('texts.custom_field');
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
@ -204,4 +204,13 @@ trait SendsEmails
|
||||
|
||||
return Domain::getEmailFromId($this->domain_id);
|
||||
}
|
||||
|
||||
public function getDailyEmailLimit()
|
||||
{
|
||||
$limit = MAX_EMAILS_SENT_PER_DAY;
|
||||
|
||||
$limit += $this->created_at->diffInMonths() * 100;
|
||||
|
||||
return min($limit, 1000);
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ class Vendor extends EntityModel
|
||||
'currency_id',
|
||||
'website',
|
||||
'transaction_name',
|
||||
'custom_value1',
|
||||
'custom_value2',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -98,10 +100,10 @@ class Vendor extends EntityModel
|
||||
self::$fieldPostalCode,
|
||||
self::$fieldCountry,
|
||||
self::$fieldNotes,
|
||||
VendorContact::$fieldFirstName,
|
||||
VendorContact::$fieldLastName,
|
||||
VendorContact::$fieldPhone,
|
||||
VendorContact::$fieldEmail,
|
||||
'contact_first_name',
|
||||
'contact_last_name',
|
||||
'contact_email',
|
||||
'contact_phone',
|
||||
];
|
||||
}
|
||||
|
||||
@ -111,11 +113,12 @@ class Vendor extends EntityModel
|
||||
public static function getImportMap()
|
||||
{
|
||||
return [
|
||||
'first' => 'first_name',
|
||||
'last' => 'last_name',
|
||||
'email' => 'email',
|
||||
'mobile|phone' => 'phone',
|
||||
'name|organization' => 'name',
|
||||
'first' => 'contact_first_name',
|
||||
'last' => 'contact_last_name',
|
||||
'email' => 'contact_email',
|
||||
'mobile|phone' => 'contact_phone',
|
||||
'work|office' => 'work_phone',
|
||||
'name|organization|vendor' => 'name',
|
||||
'street2|address2' => 'address2',
|
||||
'street|address|address1' => 'address1',
|
||||
'city' => 'city',
|
||||
|
@ -25,7 +25,7 @@ class AccountGatewayDatatable extends EntityDatatable
|
||||
$accountGateway = $this->getAccountGateway($model->id);
|
||||
if ($model->deleted_at) {
|
||||
return $model->name;
|
||||
} elseif ($model->gateway_id == GATEWAY_CUSTOM) {
|
||||
} elseif (in_array($model->gateway_id, [GATEWAY_CUSTOM1, GATEWAY_CUSTOM2, GATEWAY_CUSTOM3])) {
|
||||
$name = $accountGateway->getConfigField('name') . ' [' . trans('texts.custom') . ']';
|
||||
return link_to("gateways/{$model->public_id}/edit", $name)->toHtml();
|
||||
} elseif ($model->gateway_id != GATEWAY_WEPAY) {
|
||||
@ -191,8 +191,12 @@ class AccountGatewayDatatable extends EntityDatatable
|
||||
},
|
||||
function ($model) use ($gatewayType) {
|
||||
// Only show this action if the given gateway supports this gateway type
|
||||
if ($model->gateway_id == GATEWAY_CUSTOM) {
|
||||
return $gatewayType->id == GATEWAY_TYPE_CUSTOM;
|
||||
if ($model->gateway_id == GATEWAY_CUSTOM1) {
|
||||
return $gatewayType->id == GATEWAY_TYPE_CUSTOM1;
|
||||
} elseif ($model->gateway_id == GATEWAY_CUSTOM2) {
|
||||
return $gatewayType->id == GATEWAY_TYPE_CUSTOM2;
|
||||
} elseif ($model->gateway_id == GATEWAY_CUSTOM3) {
|
||||
return $gatewayType->id == GATEWAY_TYPE_CUSTOM3;
|
||||
} else {
|
||||
$accountGateway = $this->getAccountGateway($model->id);
|
||||
return $accountGateway->paymentDriver()->supportsGatewayType($gatewayType->id);
|
||||
@ -229,8 +233,12 @@ class AccountGatewayDatatable extends EntityDatatable
|
||||
|
||||
private function getGatewayTypes($id, $gatewayId)
|
||||
{
|
||||
if ($gatewayId == GATEWAY_CUSTOM) {
|
||||
$gatewayTypes = [GATEWAY_TYPE_CUSTOM];
|
||||
if ($gatewayId == GATEWAY_CUSTOM1) {
|
||||
$gatewayTypes = [GATEWAY_TYPE_CUSTOM1];
|
||||
} elseif ($gatewayId == GATEWAY_CUSTOM2) {
|
||||
$gatewayTypes = [GATEWAY_TYPE_CUSTOM2];
|
||||
} elseif ($gatewayId == GATEWAY_CUSTOM3) {
|
||||
$gatewayTypes = [GATEWAY_TYPE_CUSTOM3];
|
||||
} else {
|
||||
$accountGateway = $this->getAccountGateway($id);
|
||||
$paymentDriver = $accountGateway->paymentDriver();
|
||||
|
@ -47,14 +47,14 @@ class ProductDatatable extends EntityDatatable
|
||||
function ($model) {
|
||||
return $model->custom_value1;
|
||||
},
|
||||
$account->custom_invoice_item_label1
|
||||
$account->customLabel('product1')
|
||||
],
|
||||
[
|
||||
'custom_value2',
|
||||
function ($model) {
|
||||
return $model->custom_value2;
|
||||
},
|
||||
$account->custom_invoice_item_label2
|
||||
$account->customLabel('product2')
|
||||
]
|
||||
];
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Ninja\Datatables;
|
||||
|
||||
use App\Models\Task;
|
||||
use App\Models\TaskStatus;
|
||||
use Auth;
|
||||
use URL;
|
||||
use Utils;
|
||||
@ -129,4 +130,26 @@ class TaskDatatable extends EntityDatatable
|
||||
|
||||
return "<h4><div class=\"label label-{$class}\">$label</div></h4>";
|
||||
}
|
||||
|
||||
public function bulkActions()
|
||||
{
|
||||
$actions = [];
|
||||
|
||||
$statuses = TaskStatus::scope()->orderBy('sort_order')->get();
|
||||
|
||||
foreach ($statuses as $status) {
|
||||
$actions[] = [
|
||||
'label' => sprintf('%s %s', trans('texts.mark'), $status->name),
|
||||
'url' => 'javascript:submitForm_' . $this->entityType . '("update_status:' . $status->public_id . '")',
|
||||
];
|
||||
}
|
||||
|
||||
if (count($actions)) {
|
||||
$actions[] = \DropdownButton::DIVIDER;
|
||||
}
|
||||
|
||||
$actions = array_merge($actions, parent::bulkActions());
|
||||
|
||||
return $actions;
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,19 @@ class BaseTransformer extends TransformerAbstract
|
||||
return isset($this->maps[ENTITY_CLIENT][$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasVendor($name)
|
||||
{
|
||||
$name = trim(strtolower($name));
|
||||
|
||||
return isset($this->maps[ENTITY_VENDOR][$name]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
*
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Ninja\Import\CSV;
|
||||
|
||||
use Utils;
|
||||
use App\Ninja\Import\BaseTransformer;
|
||||
use League\Fractal\Resource\Item;
|
||||
|
||||
@ -18,14 +19,20 @@ class ExpenseTransformer extends BaseTransformer
|
||||
public function transform($data)
|
||||
{
|
||||
return new Item($data, function ($data) {
|
||||
$clientId = isset($data->client) ? $this->getClientId($data->client) : null;
|
||||
|
||||
return [
|
||||
'amount' => $this->getFloat($data, 'amount'),
|
||||
'vendor_id' => isset($data->vendor) ? $this->getVendorId($data->vendor) : null,
|
||||
'client_id' => isset($data->client) ? $this->getClientId($data->client) : null,
|
||||
'client_id' => $clientId,
|
||||
'expense_date' => isset($data->expense_date) ? date('Y-m-d', strtotime($data->expense_date)) : null,
|
||||
'public_notes' => $this->getString($data, 'public_notes'),
|
||||
'private_notes' => $this->getString($data, 'private_notes'),
|
||||
'expense_category_id' => isset($data->expense_category) ? $this->getExpenseCategoryId($data->expense_category) : null,
|
||||
'payment_type_id' => isset($data->payment_type) ? Utils::lookupIdInCache($data->payment_type, 'paymentTypes') : null,
|
||||
'payment_date' => isset($data->payment_date) ? date('Y-m-d', strtotime($data->payment_date)) : null,
|
||||
'transaction_reference' => $this->getString($data, 'transaction_reference'),
|
||||
'should_be_invoiced' => $clientId ? true : false,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
@ -31,12 +31,12 @@ class VendorTransformer extends BaseTransformer
|
||||
'state' => $this->getString($data, 'state'),
|
||||
'postal_code' => $this->getString($data, 'postal_code'),
|
||||
'private_notes' => $this->getString($data, 'notes'),
|
||||
'contacts' => [
|
||||
'vendor_contacts' => [
|
||||
[
|
||||
'first_name' => $this->getString($data, 'first_name'),
|
||||
'last_name' => $this->getString($data, 'last_name'),
|
||||
'email' => $this->getString($data, 'email'),
|
||||
'phone' => $this->getString($data, 'phone'),
|
||||
'first_name' => $this->getString($data, 'contact_first_name'),
|
||||
'last_name' => $this->getString($data, 'contact_last_name'),
|
||||
'email' => $this->getString($data, 'contact_email'),
|
||||
'phone' => $this->getString($data, 'contact_phone'),
|
||||
],
|
||||
],
|
||||
'country_id' => isset($data->country) ? $this->getCountryId($data->country) : null,
|
||||
|
41
app/Ninja/Import/Pancake/ClientTransformer.php
Normal file
41
app/Ninja/Import/Pancake/ClientTransformer.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Ninja\Import\Pancake;
|
||||
|
||||
use App\Ninja\Import\BaseTransformer;
|
||||
use League\Fractal\Resource\Item;
|
||||
|
||||
/**
|
||||
* Class ClientTransformer.
|
||||
*/
|
||||
class ClientTransformer extends BaseTransformer
|
||||
{
|
||||
/**
|
||||
* @param $data
|
||||
*
|
||||
* @return bool|Item
|
||||
*/
|
||||
public function transform($data)
|
||||
{
|
||||
if ($this->hasClient($data->company)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return new Item($data, function ($data) {
|
||||
return [
|
||||
'name' => $this->getString($data, 'company'),
|
||||
'work_phone' => $this->getString($data, 'telephone_number'),
|
||||
'website' => $this->getString($data, 'website_url'),
|
||||
'private_notes' => $this->getString($data, 'notes'),
|
||||
'contacts' => [
|
||||
[
|
||||
'first_name' => $this->getString($data, 'first_name'),
|
||||
'last_name' => $this->getString($data, 'last_name'),
|
||||
'email' => $this->getString($data, 'email'),
|
||||
'phone' => $this->getString($data, 'mobile_number'),
|
||||
],
|
||||
],
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
71
app/Ninja/Import/Pancake/InvoiceTransformer.php
Normal file
71
app/Ninja/Import/Pancake/InvoiceTransformer.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Ninja\Import\Pancake;
|
||||
|
||||
use App\Ninja\Import\BaseTransformer;
|
||||
use League\Fractal\Resource\Item;
|
||||
|
||||
/**
|
||||
* Class InvoiceTransformer.
|
||||
*/
|
||||
class InvoiceTransformer extends BaseTransformer
|
||||
{
|
||||
/**
|
||||
* @param $data
|
||||
*
|
||||
* @return bool|Item
|
||||
*/
|
||||
public function transform($data)
|
||||
{
|
||||
if (! $this->getClientId($data->client)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->hasInvoice($data->invoice)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($data->recurring == 'Yes') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return new Item($data, function ($data) {
|
||||
return [
|
||||
'client_id' => $this->getClientId($data->client),
|
||||
'invoice_number' => $this->getInvoiceNumber($data->invoice),
|
||||
'invoice_date' => ! empty($data->date_of_creation) ? date('Y-m-d', strtotime($data->date_of_creation)) : null,
|
||||
'due_date' => ! empty($data->due_date) ? date('Y-m-d', strtotime($data->due_date)) : null,
|
||||
'paid' => (float) $data->amount_paid,
|
||||
'public_notes' => $this->getString($data, 'notes'),
|
||||
'private_notes' => $this->getString($data, 'description'),
|
||||
'invoice_date_sql' => $data->create_date,
|
||||
'invoice_items' => [
|
||||
[
|
||||
'product_key' => $data->item_1_gross_discount > 0 ? trans('texts.discount') : $data->item_1_name,
|
||||
'notes' => $data->item_1_description,
|
||||
'cost' => (float) $data->item_1_gross_discount > 0 ? $data->item_1_gross_discount * -1 : $data->item_1_rate,
|
||||
'qty' => $data->item_1_quantity,
|
||||
],
|
||||
[
|
||||
'product_key' => $data->item_2_gross_discount > 0 ? trans('texts.discount') : $data->item_2_name,
|
||||
'notes' => $data->item_2_description,
|
||||
'cost' => (float) $data->item_2_gross_discount > 0 ? $data->item_2_gross_discount * -1 : $data->item_2_rate,
|
||||
'qty' => $data->item_2_quantity,
|
||||
],
|
||||
[
|
||||
'product_key' => $data->item_3_gross_discount > 0 ? trans('texts.discount') : $data->item_3_name,
|
||||
'notes' => $data->item_3_description,
|
||||
'cost' => (float) $data->item_3_gross_discount > 0 ? $data->item_3_gross_discount * -1 : $data->item_3_rate,
|
||||
'qty' => $data->item_3_quantity,
|
||||
],
|
||||
[
|
||||
'product_key' => $data->item_4_gross_discount > 0 ? trans('texts.discount') : $data->item_4_name,
|
||||
'notes' => $data->item_4_description,
|
||||
'cost' => (float) $data->item_4_gross_discount > 0 ? $data->item_4_gross_discount * -1 : $data->item_4_rate,
|
||||
'qty' => $data->item_4_quantity,
|
||||
],
|
||||
],
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
@ -70,9 +70,6 @@ class ContactMailer extends Mailer
|
||||
$pdfString = false;
|
||||
$ublString = false;
|
||||
|
||||
if ($account->attachPDF() && ! $proposal) {
|
||||
$pdfString = $invoice->getPDFString();
|
||||
}
|
||||
if ($account->attachUBL() && ! $proposal) {
|
||||
$ublString = dispatch(new ConvertInvoiceToUbl($invoice));
|
||||
}
|
||||
@ -100,6 +97,9 @@ class ContactMailer extends Mailer
|
||||
$isFirst = true;
|
||||
$invitations = $proposal ? $proposal->invitations : $invoice->invitations;
|
||||
foreach ($invitations as $invitation) {
|
||||
if ($account->attachPDF() && ! $proposal) {
|
||||
$pdfString = $invoice->getPDFString($invitation);
|
||||
}
|
||||
$data = [
|
||||
'pdfString' => $pdfString,
|
||||
'documentStrings' => $documentStrings,
|
||||
@ -266,6 +266,7 @@ class ContactMailer extends Mailer
|
||||
|
||||
$account->loadLocalizationSettings($client);
|
||||
$invoice = $payment->invoice;
|
||||
$invitation = $payment->invitation ?: $payment->invoice->invitations[0];
|
||||
$accountName = $account->getDisplayName();
|
||||
|
||||
if ($refunded > 0) {
|
||||
@ -282,11 +283,9 @@ class ContactMailer extends Mailer
|
||||
if ($payment->invitation) {
|
||||
$user = $payment->invitation->user;
|
||||
$contact = $payment->contact;
|
||||
$invitation = $payment->invitation;
|
||||
} else {
|
||||
$user = $payment->user;
|
||||
$contact = $client->contacts->count() ? $client->contacts[0] : '';
|
||||
$invitation = $payment->invoice->invitations[0];
|
||||
}
|
||||
|
||||
$variables = [
|
||||
@ -382,7 +381,7 @@ class ContactMailer extends Mailer
|
||||
|
||||
// http://stackoverflow.com/questions/1375501/how-do-i-throttle-my-sites-api-users
|
||||
$day = 60 * 60 * 24;
|
||||
$day_limit = MAX_EMAILS_SENT_PER_DAY;
|
||||
$day_limit = $account->getDailyEmailLimit();
|
||||
$day_throttle = Cache::get("email_day_throttle:{$key}", null);
|
||||
$last_api_request = Cache::get("last_email_request:{$key}", 0);
|
||||
$last_api_diff = time() - $last_api_request;
|
||||
|
@ -40,7 +40,10 @@ class Mailer
|
||||
$toEmail = strtolower($toEmail);
|
||||
$replyEmail = $fromEmail;
|
||||
$fromEmail = CONTACT_EMAIL;
|
||||
//\Log::info("{$toEmail} | {$replyEmail} | $fromEmail");
|
||||
|
||||
if (Utils::isSelfHost() && config('app.debug')) {
|
||||
\Log::info("Sending email - To: {$toEmail} | Reply: {$replyEmail} | From: $fromEmail");
|
||||
}
|
||||
|
||||
// Optionally send for alternate domain
|
||||
if (! empty($data['fromEmail'])) {
|
||||
|
@ -986,8 +986,14 @@ class BasePaymentDriver
|
||||
|
||||
$gatewayTypeAlias = GatewayType::getAliasFromId($gatewayTypeId);
|
||||
|
||||
if ($gatewayTypeId == GATEWAY_TYPE_CUSTOM) {
|
||||
$url = 'javascript:showCustomModal();';
|
||||
if ($gatewayTypeId == GATEWAY_TYPE_CUSTOM1) {
|
||||
$url = 'javascript:showCustom1Modal();';
|
||||
$label = e($this->accountGateway->getConfigField('name'));
|
||||
} elseif ($gatewayTypeId == GATEWAY_TYPE_CUSTOM2) {
|
||||
$url = 'javascript:showCustom2Modal();';
|
||||
$label = e($this->accountGateway->getConfigField('name'));
|
||||
} elseif ($gatewayTypeId == GATEWAY_TYPE_CUSTOM3) {
|
||||
$url = 'javascript:showCustom3Modal();';
|
||||
$label = e($this->accountGateway->getConfigField('name'));
|
||||
} else {
|
||||
$url = $this->paymentUrl($gatewayTypeAlias);
|
||||
|
@ -2,12 +2,12 @@
|
||||
|
||||
namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
class CustomPaymentDriver extends BasePaymentDriver
|
||||
class Custom1PaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
public function gatewayTypes()
|
||||
{
|
||||
return [
|
||||
GATEWAY_TYPE_CUSTOM,
|
||||
GATEWAY_TYPE_CUSTOM1,
|
||||
];
|
||||
}
|
||||
}
|
13
app/Ninja/PaymentDrivers/Custom2PaymentDriver.php
Normal file
13
app/Ninja/PaymentDrivers/Custom2PaymentDriver.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
class Custom2PaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
public function gatewayTypes()
|
||||
{
|
||||
return [
|
||||
GATEWAY_TYPE_CUSTOM2,
|
||||
];
|
||||
}
|
||||
}
|
13
app/Ninja/PaymentDrivers/Custom3PaymentDriver.php
Normal file
13
app/Ninja/PaymentDrivers/Custom3PaymentDriver.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Ninja\PaymentDrivers;
|
||||
|
||||
class Custom3PaymentDriver extends BasePaymentDriver
|
||||
{
|
||||
public function gatewayTypes()
|
||||
{
|
||||
return [
|
||||
GATEWAY_TYPE_CUSTOM3,
|
||||
];
|
||||
}
|
||||
}
|
@ -195,20 +195,20 @@ class AccountPresenter extends Presenter
|
||||
public function customTextFields()
|
||||
{
|
||||
$fields = [
|
||||
'custom_client_label1' => 'custom_client1',
|
||||
'custom_client_label2' => 'custom_client2',
|
||||
'custom_contact_label1' => 'custom_contact1',
|
||||
'custom_contact_label2' => 'custom_contact2',
|
||||
'custom_invoice_text_label1' => 'custom_invoice1',
|
||||
'custom_invoice_text_label2' => 'custom_invoice2',
|
||||
'custom_invoice_item_label1' => 'custom_product1',
|
||||
'custom_invoice_item_label2' => 'custom_product2',
|
||||
'client1' => 'custom_client1',
|
||||
'client1' => 'custom_client2',
|
||||
'contact1' => 'custom_contact1',
|
||||
'contact2' => 'custom_contact2',
|
||||
'invoice_text1' => 'custom_invoice1',
|
||||
'invoice_text2' => 'custom_invoice2',
|
||||
'product1' => 'custom_product1',
|
||||
'product2' => 'custom_product2',
|
||||
];
|
||||
$data = [];
|
||||
|
||||
foreach ($fields as $key => $val) {
|
||||
if ($this->$key) {
|
||||
$data[Utils::getCustomLabel($this->$key)] = [
|
||||
if ($label = $this->customLabel($key)) {
|
||||
$data[Utils::getCustomLabel($label)] = [
|
||||
'value' => $val,
|
||||
'name' => $val,
|
||||
];
|
||||
@ -265,55 +265,8 @@ class AccountPresenter extends Presenter
|
||||
return $url;
|
||||
}
|
||||
|
||||
|
||||
public function customClientLabel1()
|
||||
public function customLabel($field)
|
||||
{
|
||||
return Utils::getCustomLabel($this->entity->custom_client_label1);
|
||||
return Utils::getCustomLabel($this->entity->customLabel($field));
|
||||
}
|
||||
|
||||
public function customClientLabel2()
|
||||
{
|
||||
return Utils::getCustomLabel($this->entity->custom_client_label2);
|
||||
}
|
||||
|
||||
public function customContactLabel1()
|
||||
{
|
||||
return Utils::getCustomLabel($this->entity->custom_contact_label1);
|
||||
}
|
||||
|
||||
public function customContactLabel2()
|
||||
{
|
||||
return Utils::getCustomLabel($this->entity->custom_contact_label2);
|
||||
}
|
||||
|
||||
public function customInvoiceLabel1()
|
||||
{
|
||||
return Utils::getCustomLabel($this->entity->custom_invoice_label1);
|
||||
}
|
||||
|
||||
public function customInvoiceLabel2()
|
||||
{
|
||||
return Utils::getCustomLabel($this->entity->custom_invoice_label2);
|
||||
}
|
||||
|
||||
public function customInvoiceTextLabel1()
|
||||
{
|
||||
return Utils::getCustomLabel($this->entity->custom_invoice_text_label1);
|
||||
}
|
||||
|
||||
public function customInvoiceTextLabel2()
|
||||
{
|
||||
return Utils::getCustomLabel($this->entity->custom_invoice_text_label1);
|
||||
}
|
||||
|
||||
public function customProductLabel1()
|
||||
{
|
||||
return Utils::getCustomLabel($this->entity->custom_invoice_item_label1);
|
||||
}
|
||||
|
||||
public function customProductLabel2()
|
||||
{
|
||||
return Utils::getCustomLabel($this->entity->custom_invoice_item_label2);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -59,6 +59,15 @@ class ExpensePresenter extends EntityPresenter
|
||||
return $this->entity->expense_category ? $this->entity->expense_category->name : '';
|
||||
}
|
||||
|
||||
public function payment_type()
|
||||
{
|
||||
if (! $this->payment_type_id) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return Utils::getFromCache($this->payment_type_id, 'paymentTypes')->name;
|
||||
}
|
||||
|
||||
public function calendarEvent($subColors = false)
|
||||
{
|
||||
$data = parent::calendarEvent();
|
||||
|
@ -13,4 +13,31 @@ class UserPresenter extends EntityPresenter
|
||||
{
|
||||
return $this->entity->first_name . ' ' . $this->entity->last_name;
|
||||
}
|
||||
|
||||
public function statusCode()
|
||||
{
|
||||
$status = '';
|
||||
$user = $this->entity;
|
||||
$account = $user->account;
|
||||
|
||||
if ($user->confirmed) {
|
||||
$status .= 'C';
|
||||
} elseif ($user->registered) {
|
||||
$status .= 'R';
|
||||
} else {
|
||||
$status .= 'N';
|
||||
}
|
||||
|
||||
if ($account->isTrial()) {
|
||||
$status .= 'T';
|
||||
} elseif ($account->isEnterprise()) {
|
||||
$status .= 'E';
|
||||
} elseif ($account->isPro()) {
|
||||
$status .= 'P';
|
||||
} else {
|
||||
$status .= 'H';
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
|
@ -22,11 +22,11 @@ class ClientReport extends AbstractReport
|
||||
$user = auth()->user();
|
||||
$account = $user->account;
|
||||
|
||||
if ($account->custom_client_label1) {
|
||||
$columns[$account->present()->customClientLabel1] = ['columnSelector-false', 'custom'];
|
||||
if ($account->customLabel('client1')) {
|
||||
$columns[$account->present()->customLabel('client1')] = ['columnSelector-false', 'custom'];
|
||||
}
|
||||
if ($account->custom_client_label2) {
|
||||
$columns[$account->present()->customClientLabel2] = ['columnSelector-false', 'custom'];
|
||||
if ($account->customLabel('client2')) {
|
||||
$columns[$account->present()->customLabel('client2')] = ['columnSelector-false', 'custom'];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
@ -75,10 +75,10 @@ class ClientReport extends AbstractReport
|
||||
$client->user->getDisplayName(),
|
||||
];
|
||||
|
||||
if ($account->custom_client_label1) {
|
||||
if ($account->customLabel('client1')) {
|
||||
$row[] = $client->custom_value1;
|
||||
}
|
||||
if ($account->custom_client_label2) {
|
||||
if ($account->customLabel('client2')) {
|
||||
$row[] = $client->custom_value2;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,16 @@ class ExpenseReport extends AbstractReport
|
||||
'user' => ['columnSelector-false'],
|
||||
];
|
||||
|
||||
$user = auth()->user();
|
||||
$account = $user->account;
|
||||
|
||||
if ($account->customLabel('expense1')) {
|
||||
$columns[$account->present()->customLabel('expense1')] = ['columnSelector-false', 'custom'];
|
||||
}
|
||||
if ($account->customLabel('expense2')) {
|
||||
$columns[$account->present()->customLabel('expense2')] = ['columnSelector-false', 'custom'];
|
||||
}
|
||||
|
||||
if (TaxRate::scope()->count()) {
|
||||
$columns['tax'] = ['columnSelector-false'];
|
||||
}
|
||||
@ -85,6 +95,13 @@ class ExpenseReport extends AbstractReport
|
||||
$expense->user->getDisplayName(),
|
||||
];
|
||||
|
||||
if ($account->customLabel('expense1')) {
|
||||
$row[] = $expense->custom_value1;
|
||||
}
|
||||
if ($account->customLabel('expense2')) {
|
||||
$row[] = $expense->custom_value2;
|
||||
}
|
||||
|
||||
if ($hasTaxRates) {
|
||||
$row[] = $expense->present()->taxAmount;
|
||||
}
|
||||
|
@ -31,11 +31,11 @@ class InvoiceReport extends AbstractReport
|
||||
|
||||
$account = auth()->user()->account;
|
||||
|
||||
if ($account->custom_invoice_text_label1) {
|
||||
$columns[$account->present()->customInvoiceTextLabel1] = ['columnSelector-false', 'custom'];
|
||||
if ($account->customLabel('invoice_text1')) {
|
||||
$columns[$account->present()->customLabel('invoice_text1')] = ['columnSelector-false', 'custom'];
|
||||
}
|
||||
if ($account->custom_invoice_text_label1) {
|
||||
$columns[$account->present()->customInvoiceTextLabel2] = ['columnSelector-false', 'custom'];
|
||||
if ($account->customLabel('invoice_text1')) {
|
||||
$columns[$account->present()->customLabel('invoice_text2')] = ['columnSelector-false', 'custom'];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
@ -108,10 +108,10 @@ class InvoiceReport extends AbstractReport
|
||||
$row[] = $isFirst ? $account->formatMoney($invoice->getTaxTotal(), $client) : '';
|
||||
}
|
||||
|
||||
if ($account->custom_invoice_text_label1) {
|
||||
if ($account->customLabel('invoice_text1')) {
|
||||
$row[] = $invoice->custom_text_value1;
|
||||
}
|
||||
if ($account->custom_invoice_text_label2) {
|
||||
if ($account->customLabel('invoice_text2')) {
|
||||
$row[] = $invoice->custom_text_value2;
|
||||
}
|
||||
|
||||
|
@ -31,12 +31,12 @@ class ProductReport extends AbstractReport
|
||||
}
|
||||
}
|
||||
|
||||
if ($account->custom_invoice_item_label1) {
|
||||
$columns[$account->present()->customProductLabel1] = ['columnSelector-false', 'custom'];
|
||||
if ($account->customLabel('product1')) {
|
||||
$columns[$account->present()->customLabel('product1')] = ['columnSelector-false', 'custom'];
|
||||
}
|
||||
|
||||
if ($account->custom_invoice_item_label2) {
|
||||
$columns[$account->present()->customProductLabel2] = ['columnSelector-false', 'custom'];
|
||||
if ($account->customLabel('product2')) {
|
||||
$columns[$account->present()->customLabel('product2')] = ['columnSelector-false', 'custom'];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
@ -75,17 +75,14 @@ class ProductReport extends AbstractReport
|
||||
];
|
||||
|
||||
if ($account->invoice_item_taxes) {
|
||||
$row[] = $item->present()->tax1;
|
||||
if ($account->enable_second_tax_rate) {
|
||||
$row[] = $item->present()->tax2;
|
||||
}
|
||||
$row[] = Utils::roundSignificant($item->getTaxAmount(), 2);
|
||||
}
|
||||
|
||||
if ($account->custom_invoice_item_label1) {
|
||||
if ($account->customLabel('product1')) {
|
||||
$row[] = $item->custom_value1;
|
||||
}
|
||||
|
||||
if ($account->custom_invoice_item_label2) {
|
||||
if ($account->customLabel('product2')) {
|
||||
$row[] = $item->custom_value2;
|
||||
}
|
||||
|
||||
|
@ -27,11 +27,11 @@ class QuoteReport extends AbstractReport
|
||||
|
||||
$account = auth()->user()->account;
|
||||
|
||||
if ($account->custom_invoice_text_label1) {
|
||||
$columns[$account->present()->customInvoiceTextLabel1] = ['columnSelector-false', 'custom'];
|
||||
if ($account->customLabel('invoice_text1')) {
|
||||
$columns[$account->present()->customLabel('invoice_text1')] = ['columnSelector-false', 'custom'];
|
||||
}
|
||||
if ($account->custom_invoice_text_label1) {
|
||||
$columns[$account->present()->customInvoiceTextLabel2] = ['columnSelector-false', 'custom'];
|
||||
if ($account->customLabel('invoice_text1')) {
|
||||
$columns[$account->present()->customLabel('invoice_text2')] = ['columnSelector-false', 'custom'];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
@ -93,10 +93,10 @@ class QuoteReport extends AbstractReport
|
||||
$row[] = $account->formatMoney($invoice->getTaxTotal(), $client);
|
||||
}
|
||||
|
||||
if ($account->custom_invoice_text_label1) {
|
||||
if ($account->customLabel('invoice_text1')) {
|
||||
$row[] = $invoice->custom_text_value1;
|
||||
}
|
||||
if ($account->custom_invoice_text_label2) {
|
||||
if ($account->customLabel('invoice_text2')) {
|
||||
$row[] = $invoice->custom_text_value2;
|
||||
}
|
||||
|
||||
|
@ -4,12 +4,13 @@ namespace App\Ninja\Reports;
|
||||
|
||||
use App\Models\Task;
|
||||
use Utils;
|
||||
use Auth;
|
||||
|
||||
class TaskReport extends AbstractReport
|
||||
{
|
||||
public function getColumns()
|
||||
{
|
||||
return [
|
||||
$columns = [
|
||||
'client' => [],
|
||||
'start_date' => [],
|
||||
'project' => [],
|
||||
@ -18,10 +19,23 @@ class TaskReport extends AbstractReport
|
||||
'amount' => [],
|
||||
'user' => ['columnSelector-false'],
|
||||
];
|
||||
|
||||
$user = auth()->user();
|
||||
$account = $user->account;
|
||||
|
||||
if ($account->customLabel('task1')) {
|
||||
$columns[$account->present()->customLabel('task1')] = ['columnSelector-false', 'custom'];
|
||||
}
|
||||
if ($account->customLabel('task2')) {
|
||||
$columns[$account->present()->customLabel('task2')] = ['columnSelector-false', 'custom'];
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$account = Auth::user()->account;
|
||||
$startDate = date_create($this->startDate);
|
||||
$endDate = date_create($this->endDate);
|
||||
$subgroup = $this->options['subgroup'];
|
||||
@ -41,7 +55,7 @@ class TaskReport extends AbstractReport
|
||||
$currencyId = auth()->user()->account->getCurrencyId();
|
||||
}
|
||||
|
||||
$this->data[] = [
|
||||
$row = [
|
||||
$task->client ? ($this->isExport ? $task->client->getDisplayName() : $task->client->present()->link) : trans('texts.unassigned'),
|
||||
$this->isExport ? $task->getStartTime() : link_to($task->present()->url, $task->getStartTime()),
|
||||
$task->present()->project,
|
||||
@ -51,6 +65,15 @@ class TaskReport extends AbstractReport
|
||||
$task->user->getDisplayName(),
|
||||
];
|
||||
|
||||
if ($account->customLabel('task1')) {
|
||||
$row[] = $task->custom_value1;
|
||||
}
|
||||
if ($account->customLabel('task2')) {
|
||||
$row[] = $task->custom_value2;
|
||||
}
|
||||
|
||||
$this->data[] = $row;
|
||||
|
||||
$this->addToTotals($currencyId, 'duration', $duration);
|
||||
$this->addToTotals($currencyId, 'amount', $amount);
|
||||
|
||||
|
@ -169,11 +169,11 @@ class AccountRepository
|
||||
];
|
||||
|
||||
// include custom client fields in search
|
||||
if ($account->custom_client_label1) {
|
||||
$data[$account->present()->customClientLabel1] = [];
|
||||
if ($account->customLabel('client1')) {
|
||||
$data[$account->present()->customLabel('client1')] = [];
|
||||
}
|
||||
if ($account->custom_client_label2) {
|
||||
$data[$account->present()->customClientLabel2] = [];
|
||||
if ($account->customLabel('client2')) {
|
||||
$data[$account->present()->customLabel('client2')] = [];
|
||||
}
|
||||
|
||||
if ($user->hasPermission('view_all')) {
|
||||
@ -194,35 +194,37 @@ class AccountRepository
|
||||
}
|
||||
|
||||
foreach ($clients as $client) {
|
||||
if ($client->name) {
|
||||
$data['clients'][] = [
|
||||
'value' => ($client->id_number ? $client->id_number . ': ' : '') . $client->name,
|
||||
'tokens' => implode(',', [$client->name, $client->id_number, $client->vat_number, $client->work_phone]),
|
||||
'url' => $client->present()->url,
|
||||
];
|
||||
}
|
||||
if (! $client->is_deleted) {
|
||||
if ($client->name) {
|
||||
$data['clients'][] = [
|
||||
'value' => ($client->id_number ? $client->id_number . ': ' : '') . $client->name,
|
||||
'tokens' => implode(',', [$client->name, $client->id_number, $client->vat_number, $client->work_phone]),
|
||||
'url' => $client->present()->url,
|
||||
];
|
||||
}
|
||||
|
||||
if ($client->custom_value1) {
|
||||
$data[$account->present()->customClientLabel1][] = [
|
||||
'value' => "{$client->custom_value1}: " . $client->getDisplayName(),
|
||||
'tokens' => $client->custom_value1,
|
||||
'url' => $client->present()->url,
|
||||
];
|
||||
}
|
||||
if ($client->custom_value2) {
|
||||
$data[$account->present()->customClientLabel2][] = [
|
||||
'value' => "{$client->custom_value2}: " . $client->getDisplayName(),
|
||||
'tokens' => $client->custom_value2,
|
||||
'url' => $client->present()->url,
|
||||
];
|
||||
}
|
||||
if ($client->custom_value1) {
|
||||
$data[$account->present()->customLabel('client1')][] = [
|
||||
'value' => "{$client->custom_value1}: " . $client->getDisplayName(),
|
||||
'tokens' => $client->custom_value1,
|
||||
'url' => $client->present()->url,
|
||||
];
|
||||
}
|
||||
if ($client->custom_value2) {
|
||||
$data[$account->present()->customLabel('client2')][] = [
|
||||
'value' => "{$client->custom_value2}: " . $client->getDisplayName(),
|
||||
'tokens' => $client->custom_value2,
|
||||
'url' => $client->present()->url,
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($client->contacts as $contact) {
|
||||
$data['contacts'][] = [
|
||||
'value' => $contact->getSearchName(),
|
||||
'tokens' => implode(',', [$contact->first_name, $contact->last_name, $contact->email, $contact->phone]),
|
||||
'url' => $client->present()->url,
|
||||
];
|
||||
foreach ($client->contacts as $contact) {
|
||||
$data['contacts'][] = [
|
||||
'value' => $contact->getSearchName(),
|
||||
'tokens' => implode(',', [$contact->first_name, $contact->last_name, $contact->email, $contact->phone]),
|
||||
'url' => $client->present()->url,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($client->invoices as $invoice) {
|
||||
|
@ -399,7 +399,7 @@ class InvoiceRepository extends BaseRepository
|
||||
$invoice->custom_taxes2 = $account->custom_invoice_taxes2 ?: false;
|
||||
|
||||
// set the default due date
|
||||
if (empty($data['partial_due_date'])) {
|
||||
if ($entityType == ENTITY_INVOICE && empty($data['partial_due_date'])) {
|
||||
$client = Client::scope()->whereId($data['client_id'])->first();
|
||||
$invoice->due_date = $account->defaultDueDate($client);
|
||||
}
|
||||
@ -1322,10 +1322,23 @@ class InvoiceRepository extends BaseRepository
|
||||
|
||||
$data = $invoice->toArray();
|
||||
$fee = $invoice->calcGatewayFee($gatewayTypeId);
|
||||
$date = $account->getDateTime()->format($account->getCustomDateFormat());
|
||||
$feeItemLabel = $account->getLabel('gateway_fee_item') ?: ($fee >= 0 ? trans('texts.surcharge') : trans('texts.discount'));
|
||||
|
||||
if ($feeDescriptionLabel = $account->getLabel('gateway_fee_description')) {
|
||||
if (strpos($feeDescriptionLabel, '$date') !== false) {
|
||||
$feeDescriptionLabel = str_replace('$date', $date, $feeDescriptionLabel);
|
||||
} else {
|
||||
$feeDescriptionLabel .= ' • ' . $date;
|
||||
}
|
||||
} else {
|
||||
$feeDescriptionLabel = $fee >= 0 ? trans('texts.online_payment_surcharge') : trans('texts.online_payment_discount');
|
||||
$feeDescriptionLabel .= ' • ' . $date;
|
||||
}
|
||||
|
||||
$item = [];
|
||||
$item['product_key'] = $fee >= 0 ? trans('texts.surcharge') : trans('texts.discount');
|
||||
$item['notes'] = $fee >= 0 ? trans('texts.online_payment_surcharge') : trans('texts.online_payment_discount');
|
||||
$item['product_key'] = $feeItemLabel;
|
||||
$item['notes'] = $feeDescriptionLabel;
|
||||
$item['qty'] = 1;
|
||||
$item['cost'] = $fee;
|
||||
$item['tax_rate1'] = $settings->fee_tax_rate1;
|
||||
|
@ -159,6 +159,8 @@ class TaskRepository extends BaseRepository
|
||||
return $task;
|
||||
}
|
||||
|
||||
$task->fill($data);
|
||||
|
||||
if (isset($data['client'])) {
|
||||
$task->client_id = $data['client'] ? Client::getPrivateId($data['client']) : null;
|
||||
} elseif (isset($data['client_id'])) {
|
||||
|
@ -179,18 +179,10 @@ class AccountTransformer extends EntityTransformer
|
||||
'fill_products' => (bool) $account->fill_products,
|
||||
'update_products' => (bool) $account->update_products,
|
||||
'vat_number' => $account->vat_number,
|
||||
'custom_invoice_label1' => $account->custom_invoice_label1,
|
||||
'custom_invoice_label2' => $account->custom_invoice_label2,
|
||||
'custom_invoice_taxes1' => $account->custom_invoice_taxes1,
|
||||
'custom_invoice_taxes2' => $account->custom_invoice_taxes1,
|
||||
'custom_label1' => $account->custom_label1,
|
||||
'custom_label2' => $account->custom_label2,
|
||||
'custom_value1' => $account->custom_value1,
|
||||
'custom_value2' => $account->custom_value2,
|
||||
'primary_color' => $account->primary_color,
|
||||
'secondary_color' => $account->secondary_color,
|
||||
'custom_client_label1' => $account->custom_client_label1,
|
||||
'custom_client_label2' => $account->custom_client_label2,
|
||||
'hide_quantity' => (bool) $account->hide_quantity,
|
||||
'hide_paid_to_date' => (bool) $account->hide_paid_to_date,
|
||||
'invoice_number_prefix' => $account->invoice_number_prefix,
|
||||
@ -215,8 +207,6 @@ class AccountTransformer extends EntityTransformer
|
||||
'num_days_reminder1' => $account->num_days_reminder1,
|
||||
'num_days_reminder2' => $account->num_days_reminder2,
|
||||
'num_days_reminder3' => $account->num_days_reminder3,
|
||||
'custom_invoice_text_label1' => $account->custom_invoice_text_label1,
|
||||
'custom_invoice_text_label2' => $account->custom_invoice_text_label2,
|
||||
'tax_name1' => $account->tax_name1 ?: '',
|
||||
'tax_rate1' => (float) $account->tax_rate1,
|
||||
'tax_name2' => $account->tax_name2 ?: '',
|
||||
@ -245,8 +235,6 @@ class AccountTransformer extends EntityTransformer
|
||||
'show_currency_code' => (bool) $account->show_currency_code,
|
||||
'enable_portal_password' => (bool) $account->enable_portal_password,
|
||||
'send_portal_password' => (bool) $account->send_portal_password,
|
||||
'custom_invoice_item_label1' => $account->custom_invoice_item_label1,
|
||||
'custom_invoice_item_label2' => $account->custom_invoice_item_label2,
|
||||
'recurring_invoice_number_prefix' => $account->recurring_invoice_number_prefix,
|
||||
'enable_client_portal' => (bool) $account->enable_client_portal,
|
||||
'invoice_fields' => $account->invoice_fields,
|
||||
@ -277,12 +265,26 @@ class AccountTransformer extends EntityTransformer
|
||||
'gateway_fee_enabled' => (bool) $account->gateway_fee_enabled,
|
||||
'send_item_details' => (bool) $account->send_item_details,
|
||||
'reset_counter_date' => $account->reset_counter_date,
|
||||
'custom_contact_label1' => $account->custom_contact_label1,
|
||||
'custom_contact_label2' => $account->custom_contact_label2,
|
||||
'task_rate' => (float) $account->task_rate,
|
||||
'inclusive_taxes' => (bool) $account->inclusive_taxes,
|
||||
'convert_products' => (bool) $account->convert_products,
|
||||
'signature_on_pdf' => (bool) $account->signature_on_pdf,
|
||||
'custom_invoice_taxes1' => $account->custom_invoice_taxes1,
|
||||
'custom_invoice_taxes2' => $account->custom_invoice_taxes1,
|
||||
'custom_fields' => $account->custom_fields,
|
||||
'custom_messages' => $account->custom_messages,
|
||||
'custom_invoice_label1' => $account->customLabel('invoice1'),
|
||||
'custom_invoice_label2' => $account->customLabel('invoice2'),
|
||||
'custom_client_label1' => $account->customLabel('client1'),
|
||||
'custom_client_label2' => $account->customLabel('client2'),
|
||||
'custom_contact_label1' => $account->customLabel('contact1'),
|
||||
'custom_contact_label2' => $account->customLabel('contact2'),
|
||||
'custom_label1' => $account->customLabel('account1'),
|
||||
'custom_label2' => $account->customLabel('account2'),
|
||||
'custom_invoice_text_label1' => $account->customLabel('invoice_text1'),
|
||||
'custom_invoice_text_label2' => $account->customLabel('invoice_text2'),
|
||||
'custom_invoice_item_label1' => $account->customLabel('product1'),
|
||||
'custom_invoice_item_label2' => $account->customLabel('product2'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -155,6 +155,7 @@ class ClientTransformer extends EntityTransformer
|
||||
'show_tasks_in_portal' => (bool) $client->show_tasks_in_portal,
|
||||
'send_reminders' => (bool) $client->send_reminders,
|
||||
'credit_number_counter' => (int) $client->credit_number_counter,
|
||||
'custom_messages' => json_encode($client->custom_messages),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,8 @@ class ExpenseTransformer extends EntityTransformer
|
||||
'client_id' => $this->client ? $this->client->public_id : (isset($expense->client->public_id) ? (int) $expense->client->public_id : null),
|
||||
'invoice_id' => isset($expense->invoice->public_id) ? (int) $expense->invoice->public_id : null,
|
||||
'vendor_id' => isset($expense->vendor->public_id) ? (int) $expense->vendor->public_id : null,
|
||||
'custom_value1' => $expense->custom_value1,
|
||||
'custom_value2' => $expense->custom_value2,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ class ProductTransformer extends EntityTransformer
|
||||
'archived_at' => $this->getTimestamp($product->deleted_at),
|
||||
'custom_value1' => $product->custom_value1,
|
||||
'custom_value2' => $product->custom_value2,
|
||||
'is_deleted' => (bool) $product->is_deleted,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,8 @@ class ProjectTransformer extends EntityTransformer
|
||||
'due_date' => $project->due_date,
|
||||
'private_notes' => $project->private_notes,
|
||||
'budgeted_hours' => (float) $project->budgeted_hours,
|
||||
'custom_value1' => $project->custom_value1,
|
||||
'custom_value2' => $project->custom_value2,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,8 @@ class TaskTransformer extends EntityTransformer
|
||||
'is_deleted' => (bool) $task->is_deleted,
|
||||
'time_log' => $task->time_log,
|
||||
'is_running' => (bool) $task->is_running,
|
||||
'custom_value1' => $task->custom_value1,
|
||||
'custom_value2' => $task->custom_value2,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -86,6 +86,8 @@ class VendorTransformer extends EntityTransformer
|
||||
'vat_number' => $vendor->vat_number,
|
||||
'id_number' => $vendor->id_number,
|
||||
'currency_id' => (int) $vendor->currency_id,
|
||||
'custom_value1' => $vendor->custom_value1,
|
||||
'custom_value2' => $vendor->custom_value2,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -85,11 +85,11 @@ class AppServiceProvider extends ServiceProvider
|
||||
->render();
|
||||
});
|
||||
|
||||
Form::macro('emailPaymentButton', function ($link = '#') {
|
||||
Form::macro('emailPaymentButton', function ($link = '#', $label = 'pay_now') {
|
||||
return view('partials.email_button')
|
||||
->with([
|
||||
'link' => $link,
|
||||
'field' => 'pay_now',
|
||||
'field' => $label,
|
||||
'color' => '#36c157',
|
||||
])
|
||||
->render();
|
||||
|
@ -35,6 +35,9 @@ use Session;
|
||||
use stdClass;
|
||||
use Utils;
|
||||
use Carbon;
|
||||
use League\Csv\Reader;
|
||||
use League\Csv\Statement;
|
||||
|
||||
|
||||
/**
|
||||
* Class ImportService.
|
||||
@ -97,6 +100,7 @@ class ImportService
|
||||
ENTITY_PAYMENT,
|
||||
ENTITY_TASK,
|
||||
ENTITY_PRODUCT,
|
||||
ENTITY_VENDOR,
|
||||
ENTITY_EXPENSE,
|
||||
ENTITY_CUSTOMER,
|
||||
];
|
||||
@ -112,6 +116,7 @@ class ImportService
|
||||
IMPORT_INVOICEABLE,
|
||||
IMPORT_INVOICEPLANE,
|
||||
IMPORT_NUTCACHE,
|
||||
IMPORT_PANCAKE,
|
||||
IMPORT_RONIN,
|
||||
IMPORT_STRIPE,
|
||||
IMPORT_WAVE,
|
||||
@ -651,8 +656,11 @@ class ImportService
|
||||
private function getCsvData($fileName)
|
||||
{
|
||||
$this->checkForFile($fileName);
|
||||
$file = file_get_contents($fileName);
|
||||
$data = array_map("str_getcsv", preg_split('/\r*\n+|\r+/', $file));
|
||||
|
||||
$csv = Reader::createFromPath($fileName, 'r');
|
||||
//$csv->setHeaderOffset(0); //set the CSV header offset
|
||||
$stmt = new Statement();
|
||||
$data = iterator_to_array($stmt->process($csv));
|
||||
|
||||
if (count($data) > 0) {
|
||||
$headers = $data[0];
|
||||
|
@ -18,15 +18,17 @@ class TemplateService
|
||||
*/
|
||||
public function processVariables($template, array $data)
|
||||
{
|
||||
/** @var \App\Models\Account $account */
|
||||
$account = $data['account'];
|
||||
|
||||
/** @var \App\Models\Client $client */
|
||||
$client = $data['client'];
|
||||
|
||||
/** @var \App\Models\Invitation $invitation */
|
||||
$invitation = $data['invitation'];
|
||||
|
||||
/** @var \App\Models\Account $account */
|
||||
$account = ! empty($data['account']) ? $data['account'] : $invitation->account;
|
||||
|
||||
/** @var \App\Models\Client $client */
|
||||
$client = ! empty($data['client']) ? $data['client'] : $invitation->invoice->client;
|
||||
|
||||
$amount = ! empty($data['amount']) ? $data['amount'] : $invitation->invoice->getRequestedAmount();
|
||||
|
||||
// check if it's a proposal
|
||||
if ($invitation->proposal) {
|
||||
$invoice = $invitation->proposal->invoice;
|
||||
@ -59,7 +61,7 @@ class TemplateService
|
||||
'$invoiceDate' => $account->formatDate($invoice->invoice_date),
|
||||
'$contact' => $contact->getDisplayName(),
|
||||
'$firstName' => $contact->first_name,
|
||||
'$amount' => $account->formatMoney($data['amount'], $client),
|
||||
'$amount' => $account->formatMoney($amount, $client),
|
||||
'$total' => $invoice->present()->amount,
|
||||
'$balance' => $invoice->present()->balance,
|
||||
'$invoice' => $invoice->invoice_number,
|
||||
@ -72,6 +74,8 @@ class TemplateService
|
||||
'$viewButton' => Form::emailViewButton($invitation->getLink(), $entityType).'$password',
|
||||
'$paymentLink' => $invitation->getLink('payment').'$password',
|
||||
'$paymentButton' => Form::emailPaymentButton($invitation->getLink('payment')).'$password',
|
||||
'$approveLink' => $invitation->getLink('approve').'$password',
|
||||
'$approveButton' => Form::emailPaymentButton($invitation->getLink('approve'), 'approve').'$password',
|
||||
'$customClient1' => $client->custom_value1,
|
||||
'$customClient2' => $client->custom_value2,
|
||||
'$customContact1' => $contact->custom_value1,
|
||||
|
@ -41,7 +41,7 @@
|
||||
"jt.timepicker": "jquery-timepicker-jt#^1.11.12",
|
||||
"qrcode.js": "qrcode-js#*",
|
||||
"money.js": "^0.1.3",
|
||||
"grapesjs": "^0.13.8"
|
||||
"grapesjs": "^0.13.8",
|
||||
},
|
||||
"resolutions": {
|
||||
"jquery": "~1.11"
|
||||
|
@ -46,6 +46,7 @@
|
||||
"laravel/socialite": "~3.0",
|
||||
"laravel/tinker": "^1.0",
|
||||
"laravelcollective/html": "5.4.*",
|
||||
"league/csv": "^9.1",
|
||||
"league/flysystem-aws-s3-v3": "~1.0",
|
||||
"league/flysystem-rackspace": "~1.0",
|
||||
"league/fractal": "0.13.*",
|
||||
|
528
composer.lock
generated
528
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -271,6 +271,7 @@ return [
|
||||
'Utils' => App\Libraries\Utils::class,
|
||||
'DateUtils' => App\Libraries\DateUtils::class,
|
||||
'HTMLUtils' => App\Libraries\HTMLUtils::class,
|
||||
'CurlUtils' => App\Libraries\CurlUtils::class,
|
||||
'Domain' => App\Constants\Domain::class,
|
||||
'Google2FA' => PragmaRX\Google2FALaravel\Facade::class,
|
||||
|
||||
|
@ -26,4 +26,14 @@ return [
|
||||
// privacy policy
|
||||
'privacy_policy_url' => env('PRIVACY_POLICY_URL', ''),
|
||||
|
||||
// Google maps
|
||||
'google_maps_enabled' => env('GOOGLE_MAPS_ENABLED', true),
|
||||
'google_maps_api_key' => env('GOOGLE_MAPS_API_KEY', ''),
|
||||
|
||||
// Voice commands
|
||||
'voice_commands' => [
|
||||
'app_id' => env('MSBOT_LUIS_APP_ID', 'ea1cda29-5994-47c4-8c25-2b58ae7ae7a8'),
|
||||
'subscription_key' => env('MSBOT_LUIS_SUBSCRIPTION_KEY'),
|
||||
],
|
||||
|
||||
];
|
||||
|
153
database/migrations/2018_03_30_115805_add_more_custom_fields.php
Normal file
153
database/migrations/2018_03_30_115805_add_more_custom_fields.php
Normal file
@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use App\Models\Account;
|
||||
|
||||
class AddMoreCustomFields extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('accounts', function ($table) {
|
||||
$table->mediumText('custom_fields')->nullable();
|
||||
});
|
||||
|
||||
$accounts = Account::where('custom_label1', '!=', '')
|
||||
->orWhere('custom_label2', '!=', '')
|
||||
->orWhere('custom_client_label1', '!=', '')
|
||||
->orWhere('custom_client_label2', '!=', '')
|
||||
->orWhere('custom_contact_label1', '!=', '')
|
||||
->orWhere('custom_contact_label2', '!=', '')
|
||||
->orWhere('custom_invoice_label1', '!=', '')
|
||||
->orWhere('custom_invoice_label2', '!=', '')
|
||||
->orWhere('custom_invoice_text_label1', '!=', '')
|
||||
->orWhere('custom_invoice_text_label2', '!=', '')
|
||||
->orWhere('custom_invoice_item_label1', '!=', '')
|
||||
->orWhere('custom_invoice_item_label2', '!=', '')
|
||||
->orderBy('id')
|
||||
->get();
|
||||
|
||||
$fields = [
|
||||
'account1' => 'custom_label1',
|
||||
'account2' => 'custom_label2',
|
||||
'client1' => 'custom_client_label1',
|
||||
'client2' => 'custom_client_label2',
|
||||
'contact1' => 'custom_contact_label1',
|
||||
'contact2' => 'custom_contact_label2',
|
||||
'invoice1' => 'custom_invoice_label1',
|
||||
'invoice2' => 'custom_invoice_label2',
|
||||
'invoice_text1' => 'custom_invoice_text_label1',
|
||||
'invoice_text2' => 'custom_invoice_text_label2',
|
||||
'product1' => 'custom_invoice_item_label1',
|
||||
'product2' => 'custom_invoice_item_label2',
|
||||
];
|
||||
|
||||
foreach ($accounts as $account) {
|
||||
$config = [];
|
||||
|
||||
foreach ($fields as $key => $field) {
|
||||
if ($account->$field) {
|
||||
$config[$key] = $account->$field;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($config)) {
|
||||
$account->custom_fields = $config;
|
||||
$account->save();
|
||||
}
|
||||
}
|
||||
|
||||
Schema::table('accounts', function ($table) {
|
||||
$table->dropColumn('custom_label1');
|
||||
$table->dropColumn('custom_label2');
|
||||
$table->dropColumn('custom_client_label1');
|
||||
$table->dropColumn('custom_client_label2');
|
||||
$table->dropColumn('custom_contact_label1');
|
||||
$table->dropColumn('custom_contact_label2');
|
||||
$table->dropColumn('custom_invoice_label1');
|
||||
$table->dropColumn('custom_invoice_label2');
|
||||
$table->dropColumn('custom_invoice_text_label1');
|
||||
$table->dropColumn('custom_invoice_text_label2');
|
||||
$table->dropColumn('custom_invoice_item_label1');
|
||||
$table->dropColumn('custom_invoice_item_label2');
|
||||
});
|
||||
|
||||
Schema::table('accounts', function ($table) {
|
||||
$table->unsignedInteger('background_image_id')->nullable();
|
||||
$table->mediumText('custom_messages')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('clients', function ($table) {
|
||||
$table->mediumText('custom_messages')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('tasks', function ($table) {
|
||||
$table->text('custom_value1')->nullable();
|
||||
$table->text('custom_value2')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('projects', function ($table) {
|
||||
$table->text('custom_value1')->nullable();
|
||||
$table->text('custom_value2')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('expenses', function ($table) {
|
||||
$table->text('custom_value1')->nullable();
|
||||
$table->text('custom_value2')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('vendors', function ($table) {
|
||||
$table->text('custom_value1')->nullable();
|
||||
$table->text('custom_value2')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('products', function ($table) {
|
||||
$table->text('custom_value1')->nullable()->change();
|
||||
$table->text('custom_value2')->nullable()->change();
|
||||
});
|
||||
|
||||
Schema::table('clients', function ($table) {
|
||||
$table->text('custom_value1')->nullable()->change();
|
||||
$table->text('custom_value2')->nullable()->change();
|
||||
});
|
||||
|
||||
Schema::table('contacts', function ($table) {
|
||||
$table->text('custom_value1')->nullable()->change();
|
||||
$table->text('custom_value2')->nullable()->change();
|
||||
});
|
||||
|
||||
Schema::table('invoices', function ($table) {
|
||||
$table->text('custom_text_value1')->nullable()->change();
|
||||
$table->text('custom_text_value2')->nullable()->change();
|
||||
});
|
||||
|
||||
Schema::table('invoice_items', function ($table) {
|
||||
$table->text('custom_value1')->nullable()->change();
|
||||
$table->text('custom_value2')->nullable()->change();
|
||||
});
|
||||
|
||||
Schema::table('scheduled_reports', function ($table) {
|
||||
$table->string('ip')->nullable();
|
||||
});
|
||||
|
||||
DB::statement('UPDATE gateways SET provider = "Custom1" WHERE id = 62');
|
||||
DB::statement('UPDATE gateway_types SET alias = "custom1", name = "Custom 1" WHERE id = 6');
|
||||
DB::statement('ALTER TABLE recurring_expenses MODIFY COLUMN last_sent_date DATE');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -84,6 +84,7 @@ class CurrenciesSeeder extends Seeder
|
||||
['name' => 'Brunei Dollar', 'code' => 'BND', 'symbol' => 'B$', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
||||
['name' => 'Georgian Lari', 'code' => 'GEL', 'symbol' => '', 'precision' => '2', 'thousand_separator' => ' ', 'decimal_separator' => ','],
|
||||
['name' => 'Qatari Riyal', 'code' => 'QAR', 'symbol' => 'QR', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
||||
['name' => 'Honduran Lempira', 'code' => 'HNL', 'symbol' => 'L', 'precision' => '2', 'thousand_separator' => ',', 'decimal_separator' => '.'],
|
||||
];
|
||||
|
||||
foreach ($currencies as $currency) {
|
||||
|
@ -14,16 +14,18 @@ class GatewayTypesSeeder extends Seeder
|
||||
['alias' => 'paypal', 'name' => 'PayPal'],
|
||||
['alias' => 'bitcoin', 'name' => 'Bitcoin'],
|
||||
['alias' => 'dwolla', 'name' => 'Dwolla'],
|
||||
['alias' => 'custom', 'name' => 'Custom'],
|
||||
['alias' => 'custom1', 'name' => 'Custom'],
|
||||
['alias' => 'alipay', 'name' => 'Alipay'],
|
||||
['alias' => 'sofort', 'name' => 'Sofort'],
|
||||
['alias' => 'sepa', 'name' => 'SEPA'],
|
||||
['alias' => 'gocardless', 'name' => 'GoCardless'],
|
||||
['alias' => 'apple_pay', 'name' => 'Apple Pay'],
|
||||
['alias' => 'custom2', 'name' => 'Custom'],
|
||||
['alias' => 'custom3', 'name' => 'Custom'],
|
||||
];
|
||||
|
||||
foreach ($gateway_types as $gateway_type) {
|
||||
$record = GatewayType::where('name', '=', $gateway_type['name'])->first();
|
||||
$record = GatewayType::where('alias', '=', $gateway_type['alias'])->first();
|
||||
if (! $record) {
|
||||
GatewayType::create($gateway_type);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ class PaymentLibrariesSeeder extends Seeder
|
||||
['name' => 'PayPal Pro', 'provider' => 'PayPal_Pro'],
|
||||
['name' => 'Pin', 'provider' => 'Pin'],
|
||||
['name' => 'SagePay Direct', 'provider' => 'SagePay_Direct'],
|
||||
['name' => 'SagePay Server', 'provider' => 'SagePay_Server', 'is_offsite' => true],
|
||||
['name' => 'SagePay Server', 'provider' => 'SagePay_Server', 'is_offsite' => true, 'payment_library_id' => 2],
|
||||
['name' => 'SecurePay DirectPost', 'provider' => 'SecurePay_DirectPost'],
|
||||
['name' => 'Stripe', 'provider' => 'Stripe', 'sort_order' => 1],
|
||||
['name' => 'TargetPay Direct eBanking', 'provider' => 'TargetPay_Directebanking'],
|
||||
@ -42,7 +42,7 @@ class PaymentLibrariesSeeder extends Seeder
|
||||
['name' => 'moolah', 'provider' => 'AuthorizeNet_AIM'],
|
||||
['name' => 'Alipay', 'provider' => 'Alipay_Express'],
|
||||
['name' => 'Buckaroo', 'provider' => 'Buckaroo_CreditCard'],
|
||||
['name' => 'Coinbase', 'provider' => 'Coinbase'],
|
||||
['name' => 'Coinbase', 'provider' => 'Coinbase', 'is_offsite' => true],
|
||||
['name' => 'DataCash', 'provider' => 'DataCash'],
|
||||
['name' => 'Neteller', 'provider' => 'Neteller', 'payment_library_id' => 2],
|
||||
['name' => 'Pacnet', 'provider' => 'Pacnet'],
|
||||
@ -70,11 +70,13 @@ class PaymentLibrariesSeeder extends Seeder
|
||||
['name' => 'WeChat Express', 'provider' => 'WeChat_Express', 'payment_library_id' => 2],
|
||||
['name' => 'WePay', 'provider' => 'WePay', 'is_offsite' => false, 'sort_order' => 3],
|
||||
['name' => 'Braintree', 'provider' => 'Braintree', 'sort_order' => 3],
|
||||
['name' => 'Custom', 'provider' => 'Custom', 'is_offsite' => true, 'sort_order' => 20],
|
||||
['name' => 'Custom', 'provider' => 'Custom1', 'is_offsite' => true, 'sort_order' => 20],
|
||||
['name' => 'FirstData Payeezy', 'provider' => 'FirstData_Payeezy'],
|
||||
['name' => 'GoCardless', 'provider' => 'GoCardlessV2\Redirect', 'sort_order' => 9, 'is_offsite' => true],
|
||||
['name' => 'PagSeguro', 'provider' => 'PagSeguro'],
|
||||
['name' => 'PAYMILL', 'provider' => 'Paymill'],
|
||||
['name' => 'Custom', 'provider' => 'Custom2', 'is_offsite' => true, 'sort_order' => 21],
|
||||
['name' => 'Custom', 'provider' => 'Custom3', 'is_offsite' => true, 'sort_order' => 22],
|
||||
];
|
||||
|
||||
foreach ($gateways as $gateway) {
|
||||
|
File diff suppressed because one or more lines are too long
@ -57,9 +57,9 @@ author = u'Invoice Ninja'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'4.3'
|
||||
version = u'4.4'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'4.3.1'
|
||||
release = u'4.4.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
@ -117,14 +117,11 @@ You can disable the feature by adding ``GOOGLE_MAPS_ENABLED=false`` to the .env
|
||||
Voice Commands
|
||||
""""""""""""""
|
||||
|
||||
Supporting voice commands requires creating a `LUIS.ai <https://www.luis.ai/home/index>`_ app, once the app is created you can import this `model file <https://download.invoiceninja.com/luis.json>`_.
|
||||
|
||||
You'll also need to set the following values in the .env file.
|
||||
Supporting voice commands requires creating a `LUIS.ai subscription key <https://docs.microsoft.com/en-us/azure/cognitive-services/luis/azureibizasubscription>`_, then set the following values in the .env file.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
SPEECH_ENABLED=true
|
||||
MSBOT_LUIS_APP_ID=...
|
||||
MSBOT_LUIS_SUBSCRIPTION_KEY=...
|
||||
|
||||
Lock Invoices
|
||||
|
@ -20,11 +20,7 @@ For example:
|
||||
|
||||
php artisan module:install invoiceninja/sprockets --type=github
|
||||
|
||||
You can check the current module status with:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
php artisan module:list
|
||||
.. TIP:: One a module is installed it can enabled/disabled on Settings > Account Management
|
||||
|
||||
|
||||
Create Module
|
||||
@ -36,17 +32,13 @@ Run the following command to create a CRUD module:
|
||||
|
||||
php artisan ninja:make-module <module> <fields>
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
php artisan ninja:make-module Inventory 'name:string,description:text'
|
||||
|
||||
To edit the migration before it's run add ``--migrate=false``
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
php artisan ninja:make-module <module> <fields> --migrate=false
|
||||
|
||||
After making adjustments to the migration file you can run:
|
||||
To run the database migration use:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@ -55,10 +47,16 @@ After making adjustments to the migration file you can run:
|
||||
|
||||
.. Tip:: You can specify the module icon by setting a value from http://fontawesome.io/icons/ for "icon" in modules.json.
|
||||
|
||||
There are two types of modules: you can either create a standard module which displays a list of a new entity type or you can create a blank module which adds functionality. For example, a custom integration with a third-party app.
|
||||
|
||||
If you're looking for a module to work on you can see suggested issues `listed here <https://github.com/invoiceninja/invoiceninja/issues?q=is%3Aissue+is%3Aopen+label%3A%22custom+module%22>`_.
|
||||
|
||||
.. NOTE:: Our module implemention is currenty being actively worked on, you can join the discussion on our Slack group: http://slack.invoiceninja.com/
|
||||
|
||||
Share Module
|
||||
""""""""""""
|
||||
|
||||
To share your module create a new project on GitHub and then commit the code:
|
||||
To share your module create a new project on GitHub and then run the following code:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
|
@ -133,15 +133,6 @@ Directly to the left of the Balance Due section, you'll see a text box with a nu
|
||||
|
||||
.. TIP:: The Invoices page is rich in clickable links, providing you with a shortcut to relevant pages you may wish to view. For example, all invoice numbers are clickable, taking you directly to the specific invoice page, and all client names are clickable, taking you directly to the specific client summary page.
|
||||
|
||||
Invoice Preview
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Did you know that all this time you've been creating the new invoice, a live PDF preview of the invoice appears below, and it changes in real time according to the data you've entered?
|
||||
|
||||
Scroll down below the invoice data fields to check out the invoice preview.
|
||||
|
||||
But before we get there you'll see a row of colorful buttons, giving you a range of options:
|
||||
|
||||
- **Blue button – Download PDF**: Download the invoice as a PDF file. You can then print or save to your PC or mobile device.
|
||||
- **Gray button – Save Draft**: Save the latest version of the invoice. The data is saved in your Invoice Ninja account. You can return to the invoice at any time to continue working on it. Note: An invoice in the Draft stage is not viewable to the client in the client portal, and the amount on the invoice is not reflected in the client's invoicing balance.
|
||||
- **Green button – Mark Sent**: If you mark the invoice as sent, then the invoice will be viewable to your client in the client portal. The amount on the invoice will also be calculated in the client's balance data.
|
||||
|
@ -127,9 +127,3 @@ Click on More Actions to open the following action list:
|
||||
- **Delete Quote**: Want to delete the quote? Click here. The quote will be deleted and removed from the Quotes list page.
|
||||
|
||||
.. TIP:: At the left of these colorful buttons, you'll see a field with an arrow that opens a drop-down menu. This field provides you with template options for the quote design. Click on the arrow to select the desired template. When selected, the quote preview will change to reflect the new template.
|
||||
|
||||
Quote Preview
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Did you know that all this time you've been creating the new quote, a preview of the quote appears below, and it changes in real time according to the data you've entered? The PDF is created in real time; all you have to do is click Save.
|
||||
To check out the quote preview, scroll down below the invoice data fields.
|
||||
|
@ -28,8 +28,13 @@ A common error with shared hosting is "open_basedir restriction in effect", if y
|
||||
|
||||
.. TIP:: You can see the detailed changes for each release on our `GitHub release notes <https://github.com/invoiceninja/invoiceninja/releases>`_.
|
||||
|
||||
Version 4.3
|
||||
"""""""""""
|
||||
|
||||
You may need to manually delete ``bootstrap/cache/compiled.php``.
|
||||
|
||||
Version 4.0
|
||||
"""""""""""""
|
||||
"""""""""""
|
||||
|
||||
The minimum PHP version is now 7.0.0
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/css/built.css
vendored
2
public/css/built.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
resources/assets/css/style.css
vendored
4
resources/assets/css/style.css
vendored
@ -1417,3 +1417,7 @@ div.panel-body div.panel-body {
|
||||
padding-right:0;
|
||||
padding-left:0;
|
||||
}
|
||||
|
||||
.input-group-addon .glyphicon-question-sign {
|
||||
color: #117cc1;
|
||||
}
|
||||
|
@ -189,6 +189,17 @@ function GetPdfMake(invoice, javascript, callback) {
|
||||
if(!dd.defaultStyle)dd.defaultStyle = {font:NINJA.bodyFont};
|
||||
else if(!dd.defaultStyle.font)dd.defaultStyle.font = NINJA.bodyFont;
|
||||
|
||||
if (window.accountBackground) {
|
||||
var origBackground = dd.background;
|
||||
dd.background = function(currentPage) {
|
||||
var allPages = origBackground.length && origBackground[0].pages == 'all';
|
||||
return currentPage == 1 || allPages ? origBackground : false;
|
||||
}
|
||||
} else {
|
||||
// prevent unnecessarily showing blank image
|
||||
dd.background = false;
|
||||
}
|
||||
|
||||
doc = pdfMake.createPdf(dd);
|
||||
doc.save = function(fileName) {
|
||||
this.download(fileName);
|
||||
@ -228,6 +239,7 @@ NINJA.decodeJavascript = function(invoice, javascript)
|
||||
var json = {
|
||||
'accountName': account.name || ' ',
|
||||
'accountLogo': window.accountLogo ? window.accountLogo : blankImage,
|
||||
'accountBackground': window.accountBackground ? window.accountBackground : blankImage,
|
||||
'accountDetails': NINJA.accountDetails(invoice),
|
||||
'accountAddress': NINJA.accountAddress(invoice),
|
||||
'invoiceDetails': NINJA.invoiceDetails(invoice),
|
||||
@ -670,7 +682,7 @@ NINJA.invoiceLines = function(invoice, isSecondTable) {
|
||||
'product.discount',
|
||||
];
|
||||
|
||||
if (isSecondTable) {
|
||||
if (isSecondTable && invoice.hasStandard) {
|
||||
styles.push('secondTableHeader');
|
||||
}
|
||||
|
||||
@ -689,13 +701,13 @@ NINJA.invoiceLines = function(invoice, isSecondTable) {
|
||||
|
||||
if (field == 'custom_value1') {
|
||||
if (invoice.has_custom_item_value1) {
|
||||
value = NINJA.getCustomLabel(account.custom_invoice_item_label1);
|
||||
value = NINJA.getCustomLabel(account.custom_fields.product1);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else if (field == 'custom_value2') {
|
||||
if (invoice.has_custom_item_value2) {
|
||||
value = NINJA.getCustomLabel(account.custom_invoice_item_label2);
|
||||
value = NINJA.getCustomLabel(account.custom_fields.product2);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
@ -935,10 +947,10 @@ NINJA.subtotals = function(invoice, hideBalance)
|
||||
}
|
||||
|
||||
var customValue1 = NINJA.parseFloat(invoice.custom_value1);
|
||||
var customValue1Label = account.custom_invoice_label1 || invoiceLabels.surcharge;
|
||||
var customValue1Label = account.custom_fields.invoice1 || invoiceLabels.surcharge;
|
||||
|
||||
var customValue2 = NINJA.parseFloat(invoice.custom_value2);
|
||||
var customValue2Label = account.custom_invoice_label2 || invoiceLabels.surcharge;
|
||||
var customValue2Label = account.custom_fields.invoice2 || invoiceLabels.surcharge;
|
||||
|
||||
if (customValue1 && invoice.custom_taxes1 == '1') {
|
||||
data.push([{text: customValue1Label, style: ['subtotalsLabel', 'customTax1Label']}, {text: formatMoneyInvoice(invoice.custom_value1, invoice), style: ['subtotals', 'customTax1']}]);
|
||||
@ -1113,7 +1125,7 @@ NINJA.renderField = function(invoice, field, twoColumn) {
|
||||
return false;
|
||||
}
|
||||
var account = invoice.account;
|
||||
var contact = client.contacts[0];
|
||||
var contact = invoice.contact || client.contacts[0];
|
||||
var clientName = client.name || (contact.first_name || contact.last_name ? ((contact.first_name || '') + ' ' + (contact.last_name || '')) : contact.email);
|
||||
|
||||
var label = false;
|
||||
@ -1184,23 +1196,23 @@ NINJA.renderField = function(invoice, field, twoColumn) {
|
||||
} else if (field == 'client.phone') {
|
||||
value = contact.phone;
|
||||
} else if (field == 'client.custom_value1') {
|
||||
if (account.custom_client_label1 && client.custom_value1) {
|
||||
label = NINJA.getCustomLabel(account.custom_client_label1);
|
||||
if (account.custom_fields.client1 && client.custom_value1) {
|
||||
label = NINJA.getCustomLabel(account.custom_fields.client1);
|
||||
value = client.custom_value1;
|
||||
}
|
||||
} else if (field == 'client.custom_value2') {
|
||||
if (account.custom_client_label2 && client.custom_value2) {
|
||||
label = NINJA.getCustomLabel(account.custom_client_label2);
|
||||
if (account.custom_fields.client2 && client.custom_value2) {
|
||||
label = NINJA.getCustomLabel(account.custom_fields.client2);
|
||||
value = client.custom_value2;
|
||||
}
|
||||
} else if (field == 'contact.custom_value1') {
|
||||
if (account.custom_contact_label1 && contact.custom_value1) {
|
||||
label = NINJA.getCustomLabel(account.custom_contact_label1);
|
||||
if (account.custom_fields.contact1 && contact.custom_value1) {
|
||||
label = NINJA.getCustomLabel(account.custom_fields.contact1);
|
||||
value = contact.custom_value1;
|
||||
}
|
||||
} else if (field == 'contact.custom_value2') {
|
||||
if (account.custom_contact_label2 && contact.custom_value2) {
|
||||
label = NINJA.getCustomLabel(account.custom_contact_label2);
|
||||
if (account.custom_fields.contact2 && contact.custom_value2) {
|
||||
label = NINJA.getCustomLabel(account.custom_fields.contact2);
|
||||
value = contact.custom_value2;
|
||||
}
|
||||
} else if (field == 'account.company_name') {
|
||||
@ -1241,13 +1253,13 @@ NINJA.renderField = function(invoice, field, twoColumn) {
|
||||
} else if (field == 'account.country') {
|
||||
value = account.country ? account.country.name : false;
|
||||
} else if (field == 'account.custom_value1') {
|
||||
if (invoice.account.custom_label1 && invoice.account.custom_value1) {
|
||||
label = invoice.account.custom_label1;
|
||||
if (invoice.account.custom_fields.account1 && invoice.account.custom_value1) {
|
||||
label = invoice.account.custom_fields.account1;
|
||||
value = invoice.account.custom_value1;
|
||||
}
|
||||
} else if (field == 'account.custom_value2') {
|
||||
if (invoice.account.custom_label2 && invoice.account.custom_value2) {
|
||||
label = invoice.account.custom_label2;
|
||||
if (invoice.account.custom_fields.account2 && invoice.account.custom_value2) {
|
||||
label = invoice.account.custom_fields.account2;
|
||||
value = invoice.account.custom_value2;
|
||||
}
|
||||
} else if (field == 'invoice.invoice_number') {
|
||||
@ -1268,13 +1280,13 @@ NINJA.renderField = function(invoice, field, twoColumn) {
|
||||
value = invoice.due_date;
|
||||
}
|
||||
} else if (field == 'invoice.custom_text_value1') {
|
||||
if (invoice.custom_text_value1 && account.custom_invoice_text_label1) {
|
||||
label = NINJA.getCustomLabel(invoice.account.custom_invoice_text_label1);
|
||||
if (invoice.custom_text_value1 && account.custom_fields.invoice_text1) {
|
||||
label = NINJA.getCustomLabel(invoice.account.custom_fields.invoice_text1);
|
||||
value = invoice.is_recurring ? processVariables(invoice.custom_text_value1) : invoice.custom_text_value1;
|
||||
}
|
||||
} else if (field == 'invoice.custom_text_value2') {
|
||||
if (invoice.custom_text_value2 && account.custom_invoice_text_label2) {
|
||||
label = NINJA.getCustomLabel(invoice.account.custom_invoice_text_label2);
|
||||
if (invoice.custom_text_value2 && account.custom_fields.invoice_text2) {
|
||||
label = NINJA.getCustomLabel(invoice.account.custom_fields.invoice_text2);
|
||||
value = invoice.is_recurring ? processVariables(invoice.custom_text_value2) : invoice.custom_text_value2;
|
||||
}
|
||||
} else if (field == 'invoice.balance_due') {
|
||||
|
@ -1265,3 +1265,33 @@ function openUrlOnClick(url, event) {
|
||||
window.location = url;
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/11268104/497368
|
||||
function scorePassword(pass) {
|
||||
var score = 0;
|
||||
if (!pass)
|
||||
return score;
|
||||
|
||||
// award every unique letter until 5 repetitions
|
||||
var letters = new Object();
|
||||
for (var i=0; i<pass.length; i++) {
|
||||
letters[pass[i]] = (letters[pass[i]] || 0) + 1;
|
||||
score += 5.0 / letters[pass[i]];
|
||||
}
|
||||
|
||||
// bonus points for mixing it up
|
||||
var variations = {
|
||||
digits: /\d/.test(pass),
|
||||
lower: /[a-z]/.test(pass),
|
||||
upper: /[A-Z]/.test(pass),
|
||||
nonWords: /\W/.test(pass),
|
||||
}
|
||||
|
||||
variationCount = 0;
|
||||
for (var check in variations) {
|
||||
variationCount += (variations[check] == true) ? 1 : 0;
|
||||
}
|
||||
score += (variationCount - 1) * 10;
|
||||
|
||||
return parseInt(score);
|
||||
}
|
||||
|
@ -2407,6 +2407,8 @@ $LANG = array(
|
||||
'currency_barbadian_dollar' => 'Barbadian Dollar',
|
||||
'currency_brunei_dollar' => 'Brunei Dollar',
|
||||
'currency_georgian_lari' => 'Georgian Lari',
|
||||
'currency_qatari_riyal' => 'Qatari Riyal',
|
||||
'currency_honduran_lempira' => 'Honduran Lempira',
|
||||
|
||||
'review_app_help' => 'We hope you\'re enjoying using the app.<br/>If you\'d consider :link we\'d greatly appreciate it!',
|
||||
'writing_a_review' => 'writing a review',
|
||||
@ -2423,8 +2425,8 @@ $LANG = array(
|
||||
'contact_custom1' => 'Contact First Custom',
|
||||
'contact_custom2' => 'Contact Second Custom',
|
||||
'currency' => 'Currency',
|
||||
'ofx_help' => 'In most cases the default values should work, if you\'re unable to connect it may help to :link.',
|
||||
'adjust_the_settings' => 'adjust the settings',
|
||||
'ofx_help' => 'To troubleshoot check for comments on :ofxhome_link and test with :ofxget_link.',
|
||||
'comments' => 'comments',
|
||||
|
||||
'item_product' => 'Item Product',
|
||||
'item_notes' => 'Item Notes',
|
||||
@ -2652,7 +2654,7 @@ $LANG = array(
|
||||
'signature_on_pdf_help' => 'Show the client signature on the invoice/quote PDF.',
|
||||
'expired_white_label' => 'The white label license has expired',
|
||||
'return_to_login' => 'Return to Login',
|
||||
'convert_products_tip' => 'Note: add a custom field named ":name" to see the exchange rate.',
|
||||
'convert_products_tip' => 'Note: add a :link named ":name" to see the exchange rate.',
|
||||
'amount_greater_than_balance' => 'The amount is greater than the invoice balance, a credit will be created with the remaining amount.',
|
||||
'custom_fields_tip' => 'Use <code>Label|Option1,Option2</code> to show a select box.',
|
||||
'client_information' => 'Client Information',
|
||||
@ -2793,9 +2795,50 @@ $LANG = array(
|
||||
'purge_client_warning' => 'All related records (invoices, tasks, expenses, documents, etc) will also be deleted.',
|
||||
'clone_product' => 'Clone Product',
|
||||
'item_details' => 'Item Details',
|
||||
'send_item_details_help' => 'Send the line item details to the payment gateway.',
|
||||
'send_item_details_help' => 'Send line item details to the payment gateway.',
|
||||
'view_proposal' => 'View Proposal',
|
||||
'view_in_portal' => 'View in Portal',
|
||||
'cookie_message' => 'This website uses cookies to ensure you get the best experience on our website.',
|
||||
'got_it' => 'Got it!',
|
||||
'vendor_will_create' => 'vendor will be created',
|
||||
'vendors_will_create' => 'vendors will be created',
|
||||
'created_vendors' => 'Successfully created :count vendor(s)',
|
||||
'import_vendors' => 'Import Vendors',
|
||||
'company' => 'Company',
|
||||
'client_field' => 'Client Field',
|
||||
'contact_field' => 'Contact Field',
|
||||
'product_field' => 'Product Field',
|
||||
'task_field' => 'Task Field',
|
||||
'project_field' => 'Project Field',
|
||||
'expense_field' => 'Expense Field',
|
||||
'vendor_field' => 'Vendor Field',
|
||||
'company_field' => 'Company Field',
|
||||
'invoice_field' => 'Invoice Field',
|
||||
'invoice_surcharge' => 'Invoice Surcharge',
|
||||
'custom_task_fields_help' => 'Add a field when creating a task.',
|
||||
'custom_project_fields_help' => 'Add a field when creating a project.',
|
||||
'custom_expense_fields_help' => 'Add a field when creating an expense.',
|
||||
'custom_vendor_fields_help' => 'Add a field when creating a vendor.',
|
||||
'messages' => 'Messages',
|
||||
'unpaid_invoice' => 'Unpaid Invoice',
|
||||
'paid_invoice' => 'Paid Invoice',
|
||||
'unapproved_quote' => 'Unapproved Quote',
|
||||
'unapproved_proposal' => 'Unapproved Proposal',
|
||||
'autofills_city_state' => 'Auto-fills city/state',
|
||||
'no_match_found' => 'No match found',
|
||||
'password_strength' => 'Password Strength',
|
||||
'strength_weak' => 'Weak',
|
||||
'strength_good' => 'Good',
|
||||
'strength_strong' => 'Strong',
|
||||
'mark' => 'Mark',
|
||||
'updated_task_status' => 'Successfully update task status',
|
||||
'background_image' => 'Background Image',
|
||||
'background_image_help' => 'Use the :link to manage your images, we recommend using a small file.',
|
||||
'proposal_editor' => 'proposal editor',
|
||||
'background' => 'Background',
|
||||
'guide' => 'Guide',
|
||||
'gateway_fee_item' => 'Gateway Fee Item',
|
||||
'gateway_fee_description' => 'Gateway Fee Surcharge',
|
||||
|
||||
);
|
||||
|
||||
|
@ -2409,6 +2409,8 @@ $LANG = array(
|
||||
'currency_barbadian_dollar' => 'Barbadian Dollar',
|
||||
'currency_brunei_dollar' => 'Brunei Dollar',
|
||||
'currency_georgian_lari' => 'Georgian Lari',
|
||||
'currency_qatari_riyal' => 'Qatari Riyal',
|
||||
'currency_honduran_lempira' => 'Honduran Lempira',
|
||||
|
||||
'review_app_help' => 'We hope you\'re enjoying using the app.<br/>If you\'d consider :link we\'d greatly appreciate it!',
|
||||
'writing_a_review' => 'writing a review',
|
||||
@ -2425,8 +2427,8 @@ $LANG = array(
|
||||
'contact_custom1' => 'Contact First Custom',
|
||||
'contact_custom2' => 'Contact Second Custom',
|
||||
'currency' => 'Currency',
|
||||
'ofx_help' => 'In most cases the default values should work, if you\'re unable to connect it may help to :link.',
|
||||
'adjust_the_settings' => 'adjust the settings',
|
||||
'ofx_help' => 'To troubleshoot check for comments on :ofxhome_link and test with :ofxget_link.',
|
||||
'comments' => 'comments',
|
||||
|
||||
'item_product' => 'Item Product',
|
||||
'item_notes' => 'Item Notes',
|
||||
@ -2654,7 +2656,7 @@ $LANG = array(
|
||||
'signature_on_pdf_help' => 'Show the client signature on the invoice/quote PDF.',
|
||||
'expired_white_label' => 'The white label license has expired',
|
||||
'return_to_login' => 'Return to Login',
|
||||
'convert_products_tip' => 'Note: add a custom field named ":name" to see the exchange rate.',
|
||||
'convert_products_tip' => 'Note: add a :link named ":name" to see the exchange rate.',
|
||||
'amount_greater_than_balance' => 'The amount is greater than the invoice balance, a credit will be created with the remaining amount.',
|
||||
'custom_fields_tip' => 'Use <code>Label|Option1,Option2</code> to show a select box.',
|
||||
'client_information' => 'Client Information',
|
||||
@ -2795,9 +2797,50 @@ $LANG = array(
|
||||
'purge_client_warning' => 'All related records (invoices, tasks, expenses, documents, etc) will also be deleted.',
|
||||
'clone_product' => 'Clone Product',
|
||||
'item_details' => 'Item Details',
|
||||
'send_item_details_help' => 'Send the line item details to the payment gateway.',
|
||||
'send_item_details_help' => 'Send line item details to the payment gateway.',
|
||||
'view_proposal' => 'View Proposal',
|
||||
'view_in_portal' => 'View in Portal',
|
||||
'cookie_message' => 'This website uses cookies to ensure you get the best experience on our website.',
|
||||
'got_it' => 'Got it!',
|
||||
'vendor_will_create' => 'vendor will be created',
|
||||
'vendors_will_create' => 'vendors will be created',
|
||||
'created_vendors' => 'Successfully created :count vendor(s)',
|
||||
'import_vendors' => 'Import Vendors',
|
||||
'company' => 'Company',
|
||||
'client_field' => 'Client Field',
|
||||
'contact_field' => 'Contact Field',
|
||||
'product_field' => 'Product Field',
|
||||
'task_field' => 'Task Field',
|
||||
'project_field' => 'Project Field',
|
||||
'expense_field' => 'Expense Field',
|
||||
'vendor_field' => 'Vendor Field',
|
||||
'company_field' => 'Company Field',
|
||||
'invoice_field' => 'Invoice Field',
|
||||
'invoice_surcharge' => 'Invoice Surcharge',
|
||||
'custom_task_fields_help' => 'Add a field when creating a task.',
|
||||
'custom_project_fields_help' => 'Add a field when creating a project.',
|
||||
'custom_expense_fields_help' => 'Add a field when creating an expense.',
|
||||
'custom_vendor_fields_help' => 'Add a field when creating a vendor.',
|
||||
'messages' => 'Messages',
|
||||
'unpaid_invoice' => 'Unpaid Invoice',
|
||||
'paid_invoice' => 'Paid Invoice',
|
||||
'unapproved_quote' => 'Unapproved Quote',
|
||||
'unapproved_proposal' => 'Unapproved Proposal',
|
||||
'autofills_city_state' => 'Auto-fills city/state',
|
||||
'no_match_found' => 'No match found',
|
||||
'password_strength' => 'Password Strength',
|
||||
'strength_weak' => 'Weak',
|
||||
'strength_good' => 'Good',
|
||||
'strength_strong' => 'Strong',
|
||||
'mark' => 'Mark',
|
||||
'updated_task_status' => 'Successfully update task status',
|
||||
'background_image' => 'Background Image',
|
||||
'background_image_help' => 'Use the :link to manage your images, we recommend using a small file.',
|
||||
'proposal_editor' => 'proposal editor',
|
||||
'background' => 'Background',
|
||||
'guide' => 'Guide',
|
||||
'gateway_fee_item' => 'Gateway Fee Item',
|
||||
'gateway_fee_description' => 'Gateway Fee Surcharge',
|
||||
|
||||
);
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user