mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-09-20 16:31:33 +02:00
Merge branch 'release-3.0.0'
This commit is contained in:
commit
7a744680ae
@ -26,9 +26,14 @@ MAILGUN_SECRET=
|
||||
#POSTMARK_API_TOKEN=
|
||||
|
||||
PHANTOMJS_CLOUD_KEY='a-demo-key-with-low-quota-per-ip-address'
|
||||
#PHANTOMJS_BIN_PATH=/usr/local/bin/phantomjs
|
||||
|
||||
LOG=single
|
||||
REQUIRE_HTTPS=false
|
||||
API_SECRET=password
|
||||
IOS_DEVICE=
|
||||
ANDROID_DEVICE=
|
||||
FCM_API_TOKEN=
|
||||
|
||||
#TRUSTED_PROXIES=
|
||||
|
||||
@ -46,6 +51,7 @@ API_SECRET=password
|
||||
#GOOGLE_CLIENT_SECRET=
|
||||
#GOOGLE_OAUTH_REDIRECT=http://ninja.dev/auth/google
|
||||
|
||||
GOOGLE_MAPS_ENABLED=true
|
||||
#GOOGLE_MAPS_API_KEY=
|
||||
|
||||
#S3_KEY=
|
||||
|
@ -6,7 +6,8 @@ php:
|
||||
# - 5.5.9
|
||||
# - 5.6
|
||||
# - 5.6
|
||||
- 7.0
|
||||
# - 7.0
|
||||
- 7.1
|
||||
# - hhvm
|
||||
|
||||
addons:
|
||||
|
@ -6,7 +6,6 @@ use App\Ninja\Repositories\AccountRepository;
|
||||
use App\Services\PaymentService;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Account;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class ChargeRenewalInvoices
|
||||
@ -81,11 +80,10 @@ class ChargeRenewalInvoices extends Command
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->info("Charging invoice {$invoice->invoice_number}");
|
||||
$this->paymentService->autoBillInvoice($invoice);
|
||||
} catch (Exception $exception) {
|
||||
$this->info('Error: ' . $exception->getMessage());
|
||||
$this->info("Charging invoice {$invoice->invoice_number}");
|
||||
if ( ! $this->paymentService->autoBillInvoice($invoice)) {
|
||||
$this->info('Failed to auto-bill, emailing invoice');
|
||||
$this->mailer->sendInvoice($invoice);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use DB;
|
||||
use Mail;
|
||||
use Utils;
|
||||
use Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
@ -139,20 +140,26 @@ class CheckData extends Command {
|
||||
ENTITY_VENDOR,
|
||||
ENTITY_INVOICE,
|
||||
ENTITY_USER
|
||||
],
|
||||
'products' => [
|
||||
ENTITY_USER,
|
||||
],
|
||||
'expense_categories' => [
|
||||
ENTITY_USER,
|
||||
],
|
||||
'projects' => [
|
||||
ENTITY_USER,
|
||||
ENTITY_CLIENT,
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($tables as $table => $entityTypes) {
|
||||
foreach ($entityTypes as $entityType) {
|
||||
$tableName = Utils::pluralizeEntityType($entityType);
|
||||
$records = DB::table($table)
|
||||
->join("{$entityType}s", "{$entityType}s.id", '=', "{$table}.{$entityType}_id");
|
||||
|
||||
if ($entityType != ENTITY_CLIENT) {
|
||||
$records = $records->join('clients', 'clients.id', '=', "{$table}.client_id");
|
||||
}
|
||||
|
||||
$records = $records->where("{$table}.account_id", '!=', DB::raw("{$entityType}s.account_id"))
|
||||
->get(["{$table}.id", 'clients.account_id', 'clients.user_id']);
|
||||
->join($tableName, "{$tableName}.id", '=', "{$table}.{$entityType}_id")
|
||||
->where("{$table}.account_id", '!=', DB::raw("{$tableName}.account_id"))
|
||||
->get(["{$table}.id"]);
|
||||
|
||||
if (count($records)) {
|
||||
$this->isValid = false;
|
||||
|
582
app/Constants.php
Normal file
582
app/Constants.php
Normal file
@ -0,0 +1,582 @@
|
||||
<?php
|
||||
|
||||
if (!defined('APP_NAME'))
|
||||
{
|
||||
define('APP_NAME', env('APP_NAME', 'Invoice Ninja'));
|
||||
define('CONTACT_EMAIL', env('MAIL_FROM_ADDRESS', env('MAIL_USERNAME')));
|
||||
define('CONTACT_NAME', env('MAIL_FROM_NAME'));
|
||||
define('SITE_URL', env('APP_URL'));
|
||||
|
||||
define('ENV_DEVELOPMENT', 'local');
|
||||
define('ENV_STAGING', 'staging');
|
||||
|
||||
define('RECENTLY_VIEWED', 'recent_history');
|
||||
|
||||
define('ENTITY_CLIENT', 'client');
|
||||
define('ENTITY_CONTACT', 'contact');
|
||||
define('ENTITY_INVOICE', 'invoice');
|
||||
define('ENTITY_DOCUMENT', 'document');
|
||||
define('ENTITY_INVOICE_ITEM', 'invoice_item');
|
||||
define('ENTITY_INVITATION', 'invitation');
|
||||
define('ENTITY_RECURRING_INVOICE', 'recurring_invoice');
|
||||
define('ENTITY_PAYMENT', 'payment');
|
||||
define('ENTITY_CREDIT', 'credit');
|
||||
define('ENTITY_QUOTE', 'quote');
|
||||
define('ENTITY_TASK', 'task');
|
||||
define('ENTITY_ACCOUNT_GATEWAY', 'account_gateway');
|
||||
define('ENTITY_USER', 'user');
|
||||
define('ENTITY_TOKEN', 'token');
|
||||
define('ENTITY_TAX_RATE', 'tax_rate');
|
||||
define('ENTITY_PRODUCT', 'product');
|
||||
define('ENTITY_ACTIVITY', 'activity');
|
||||
define('ENTITY_VENDOR', 'vendor');
|
||||
define('ENTITY_VENDOR_ACTIVITY', 'vendor_activity');
|
||||
define('ENTITY_EXPENSE', 'expense');
|
||||
define('ENTITY_PAYMENT_TERM', 'payment_term');
|
||||
define('ENTITY_EXPENSE_ACTIVITY', 'expense_activity');
|
||||
define('ENTITY_BANK_ACCOUNT', 'bank_account');
|
||||
define('ENTITY_BANK_SUBACCOUNT', 'bank_subaccount');
|
||||
define('ENTITY_EXPENSE_CATEGORY', 'expense_category');
|
||||
define('ENTITY_PROJECT', 'project');
|
||||
|
||||
define('INVOICE_TYPE_STANDARD', 1);
|
||||
define('INVOICE_TYPE_QUOTE', 2);
|
||||
|
||||
define('PERSON_CONTACT', 'contact');
|
||||
define('PERSON_USER', 'user');
|
||||
define('PERSON_VENDOR_CONTACT','vendorcontact');
|
||||
|
||||
define('BASIC_SETTINGS', 'basic_settings');
|
||||
define('ADVANCED_SETTINGS', 'advanced_settings');
|
||||
|
||||
define('ACCOUNT_COMPANY_DETAILS', 'company_details');
|
||||
define('ACCOUNT_USER_DETAILS', 'user_details');
|
||||
define('ACCOUNT_LOCALIZATION', 'localization');
|
||||
define('ACCOUNT_NOTIFICATIONS', 'notifications');
|
||||
define('ACCOUNT_IMPORT_EXPORT', 'import_export');
|
||||
define('ACCOUNT_MANAGEMENT', 'account_management');
|
||||
define('ACCOUNT_PAYMENTS', 'online_payments');
|
||||
define('ACCOUNT_BANKS', 'bank_accounts');
|
||||
define('ACCOUNT_IMPORT_EXPENSES', 'import_expenses');
|
||||
define('ACCOUNT_MAP', 'import_map');
|
||||
define('ACCOUNT_EXPORT', 'export');
|
||||
define('ACCOUNT_TAX_RATES', 'tax_rates');
|
||||
define('ACCOUNT_PRODUCTS', 'products');
|
||||
define('ACCOUNT_ADVANCED_SETTINGS', 'advanced_settings');
|
||||
define('ACCOUNT_INVOICE_SETTINGS', 'invoice_settings');
|
||||
define('ACCOUNT_INVOICE_DESIGN', 'invoice_design');
|
||||
define('ACCOUNT_CLIENT_PORTAL', 'client_portal');
|
||||
define('ACCOUNT_EMAIL_SETTINGS', 'email_settings');
|
||||
define('ACCOUNT_REPORTS', 'reports');
|
||||
define('ACCOUNT_USER_MANAGEMENT', 'user_management');
|
||||
define('ACCOUNT_DATA_VISUALIZATIONS', 'data_visualizations');
|
||||
define('ACCOUNT_TEMPLATES_AND_REMINDERS', 'templates_and_reminders');
|
||||
define('ACCOUNT_API_TOKENS', 'api_tokens');
|
||||
define('ACCOUNT_CUSTOMIZE_DESIGN', 'customize_design');
|
||||
define('ACCOUNT_SYSTEM_SETTINGS', 'system_settings');
|
||||
define('ACCOUNT_PAYMENT_TERMS','payment_terms');
|
||||
|
||||
define('ACTION_RESTORE', 'restore');
|
||||
define('ACTION_ARCHIVE', 'archive');
|
||||
define('ACTION_CLONE', 'clone');
|
||||
define('ACTION_CONVERT', 'convert');
|
||||
define('ACTION_DELETE', 'delete');
|
||||
|
||||
define('ACTIVITY_TYPE_CREATE_CLIENT', 1);
|
||||
define('ACTIVITY_TYPE_ARCHIVE_CLIENT', 2);
|
||||
define('ACTIVITY_TYPE_DELETE_CLIENT', 3);
|
||||
define('ACTIVITY_TYPE_CREATE_INVOICE', 4);
|
||||
define('ACTIVITY_TYPE_UPDATE_INVOICE', 5);
|
||||
define('ACTIVITY_TYPE_EMAIL_INVOICE', 6);
|
||||
define('ACTIVITY_TYPE_VIEW_INVOICE', 7);
|
||||
define('ACTIVITY_TYPE_ARCHIVE_INVOICE', 8);
|
||||
define('ACTIVITY_TYPE_DELETE_INVOICE', 9);
|
||||
define('ACTIVITY_TYPE_CREATE_PAYMENT', 10);
|
||||
//define('ACTIVITY_TYPE_UPDATE_PAYMENT', 11);
|
||||
define('ACTIVITY_TYPE_ARCHIVE_PAYMENT', 12);
|
||||
define('ACTIVITY_TYPE_DELETE_PAYMENT', 13);
|
||||
define('ACTIVITY_TYPE_CREATE_CREDIT', 14);
|
||||
//define('ACTIVITY_TYPE_UPDATE_CREDIT', 15);
|
||||
define('ACTIVITY_TYPE_ARCHIVE_CREDIT', 16);
|
||||
define('ACTIVITY_TYPE_DELETE_CREDIT', 17);
|
||||
define('ACTIVITY_TYPE_CREATE_QUOTE', 18);
|
||||
define('ACTIVITY_TYPE_UPDATE_QUOTE', 19);
|
||||
define('ACTIVITY_TYPE_EMAIL_QUOTE', 20);
|
||||
define('ACTIVITY_TYPE_VIEW_QUOTE', 21);
|
||||
define('ACTIVITY_TYPE_ARCHIVE_QUOTE', 22);
|
||||
define('ACTIVITY_TYPE_DELETE_QUOTE', 23);
|
||||
define('ACTIVITY_TYPE_RESTORE_QUOTE', 24);
|
||||
define('ACTIVITY_TYPE_RESTORE_INVOICE', 25);
|
||||
define('ACTIVITY_TYPE_RESTORE_CLIENT', 26);
|
||||
define('ACTIVITY_TYPE_RESTORE_PAYMENT', 27);
|
||||
define('ACTIVITY_TYPE_RESTORE_CREDIT', 28);
|
||||
define('ACTIVITY_TYPE_APPROVE_QUOTE', 29);
|
||||
define('ACTIVITY_TYPE_CREATE_VENDOR', 30);
|
||||
define('ACTIVITY_TYPE_ARCHIVE_VENDOR', 31);
|
||||
define('ACTIVITY_TYPE_DELETE_VENDOR', 32);
|
||||
define('ACTIVITY_TYPE_RESTORE_VENDOR', 33);
|
||||
define('ACTIVITY_TYPE_CREATE_EXPENSE', 34);
|
||||
define('ACTIVITY_TYPE_ARCHIVE_EXPENSE', 35);
|
||||
define('ACTIVITY_TYPE_DELETE_EXPENSE', 36);
|
||||
define('ACTIVITY_TYPE_RESTORE_EXPENSE', 37);
|
||||
define('ACTIVITY_TYPE_VOIDED_PAYMENT', 39);
|
||||
define('ACTIVITY_TYPE_REFUNDED_PAYMENT', 40);
|
||||
define('ACTIVITY_TYPE_FAILED_PAYMENT', 41);
|
||||
define('ACTIVITY_TYPE_CREATE_TASK', 42);
|
||||
define('ACTIVITY_TYPE_UPDATE_TASK', 43);
|
||||
define('ACTIVITY_TYPE_ARCHIVE_TASK', 44);
|
||||
define('ACTIVITY_TYPE_DELETE_TASK', 45);
|
||||
define('ACTIVITY_TYPE_RESTORE_TASK', 46);
|
||||
define('ACTIVITY_TYPE_UPDATE_EXPENSE', 47);
|
||||
|
||||
|
||||
define('DEFAULT_INVOICE_NUMBER', '0001');
|
||||
define('RECENTLY_VIEWED_LIMIT', 20);
|
||||
define('LOGGED_ERROR_LIMIT', 100);
|
||||
define('RANDOM_KEY_LENGTH', 32);
|
||||
define('MAX_NUM_USERS', 20);
|
||||
define('MAX_IMPORT_ROWS', 1000);
|
||||
define('MAX_SUBDOMAIN_LENGTH', 30);
|
||||
define('MAX_IFRAME_URL_LENGTH', 250);
|
||||
define('MAX_LOGO_FILE_SIZE', 200); // KB
|
||||
define('MAX_FAILED_LOGINS', 10);
|
||||
define('MAX_INVOICE_ITEMS', env('MAX_INVOICE_ITEMS', 100));
|
||||
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)
|
||||
define('DOCUMENT_PREVIEW_SIZE', env('DOCUMENT_PREVIEW_SIZE', 300));// pixels
|
||||
define('DEFAULT_FONT_SIZE', 9);
|
||||
define('DEFAULT_HEADER_FONT', 1);// Roboto
|
||||
define('DEFAULT_BODY_FONT', 1);// Roboto
|
||||
define('DEFAULT_SEND_RECURRING_HOUR', 8);
|
||||
|
||||
define('IMPORT_CSV', 'CSV');
|
||||
define('IMPORT_JSON', 'JSON');
|
||||
define('IMPORT_FRESHBOOKS', 'FreshBooks');
|
||||
define('IMPORT_WAVE', 'Wave');
|
||||
define('IMPORT_RONIN', 'Ronin');
|
||||
define('IMPORT_HIVEAGE', 'Hiveage');
|
||||
define('IMPORT_ZOHO', 'Zoho');
|
||||
define('IMPORT_NUTCACHE', 'Nutcache');
|
||||
define('IMPORT_INVOICEABLE', 'Invoiceable');
|
||||
define('IMPORT_HARVEST', 'Harvest');
|
||||
|
||||
define('MAX_NUM_CLIENTS', 100);
|
||||
define('MAX_NUM_CLIENTS_PRO', 20000);
|
||||
define('MAX_NUM_CLIENTS_LEGACY', 500);
|
||||
define('MAX_INVOICE_AMOUNT', 1000000000);
|
||||
define('LEGACY_CUTOFF', 57800);
|
||||
define('ERROR_DELAY', 3);
|
||||
|
||||
define('MAX_NUM_VENDORS', 100);
|
||||
define('MAX_NUM_VENDORS_PRO', 20000);
|
||||
|
||||
define('STATUS_ACTIVE', 'active');
|
||||
define('STATUS_ARCHIVED', 'archived');
|
||||
define('STATUS_DELETED', 'deleted');
|
||||
|
||||
define('INVOICE_STATUS_DRAFT', 1);
|
||||
define('INVOICE_STATUS_SENT', 2);
|
||||
define('INVOICE_STATUS_VIEWED', 3);
|
||||
define('INVOICE_STATUS_APPROVED', 4);
|
||||
define('INVOICE_STATUS_PARTIAL', 5);
|
||||
define('INVOICE_STATUS_PAID', 6);
|
||||
define('INVOICE_STATUS_OVERDUE', 7);
|
||||
|
||||
define('PAYMENT_STATUS_PENDING', 1);
|
||||
define('PAYMENT_STATUS_VOIDED', 2);
|
||||
define('PAYMENT_STATUS_FAILED', 3);
|
||||
define('PAYMENT_STATUS_COMPLETED', 4);
|
||||
define('PAYMENT_STATUS_PARTIALLY_REFUNDED', 5);
|
||||
define('PAYMENT_STATUS_REFUNDED', 6);
|
||||
|
||||
define('TASK_STATUS_LOGGED', 1);
|
||||
define('TASK_STATUS_RUNNING', 2);
|
||||
define('TASK_STATUS_INVOICED', 3);
|
||||
define('TASK_STATUS_PAID', 4);
|
||||
|
||||
define('EXPENSE_STATUS_LOGGED', 1);
|
||||
define('EXPENSE_STATUS_INVOICED', 2);
|
||||
define('EXPENSE_STATUS_PAID', 3);
|
||||
|
||||
define('CUSTOM_DESIGN', 11);
|
||||
|
||||
define('FREQUENCY_WEEKLY', 1);
|
||||
define('FREQUENCY_TWO_WEEKS', 2);
|
||||
define('FREQUENCY_FOUR_WEEKS', 3);
|
||||
define('FREQUENCY_MONTHLY', 4);
|
||||
define('FREQUENCY_THREE_MONTHS', 5);
|
||||
define('FREQUENCY_SIX_MONTHS', 6);
|
||||
define('FREQUENCY_ANNUALLY', 7);
|
||||
|
||||
define('SESSION_TIMEZONE', 'timezone');
|
||||
define('SESSION_CURRENCY', 'currency');
|
||||
define('SESSION_CURRENCY_DECORATOR', 'currency_decorator');
|
||||
define('SESSION_DATE_FORMAT', 'dateFormat');
|
||||
define('SESSION_DATE_PICKER_FORMAT', 'datePickerFormat');
|
||||
define('SESSION_DATETIME_FORMAT', 'datetimeFormat');
|
||||
define('SESSION_COUNTER', 'sessionCounter');
|
||||
define('SESSION_LOCALE', 'sessionLocale');
|
||||
define('SESSION_USER_ACCOUNTS', 'userAccounts');
|
||||
define('SESSION_REFERRAL_CODE', 'referralCode');
|
||||
define('SESSION_LEFT_SIDEBAR', 'showLeftSidebar');
|
||||
define('SESSION_RIGHT_SIDEBAR', 'showRightSidebar');
|
||||
|
||||
define('SESSION_LAST_REQUEST_PAGE', 'SESSION_LAST_REQUEST_PAGE');
|
||||
define('SESSION_LAST_REQUEST_TIME', 'SESSION_LAST_REQUEST_TIME');
|
||||
|
||||
define('CURRENCY_DOLLAR', 1);
|
||||
define('CURRENCY_EURO', 3);
|
||||
|
||||
define('DEFAULT_TIMEZONE', 'US/Eastern');
|
||||
define('DEFAULT_COUNTRY', 840); // United Stated
|
||||
define('DEFAULT_CURRENCY', CURRENCY_DOLLAR);
|
||||
define('DEFAULT_LANGUAGE', 1); // English
|
||||
define('DEFAULT_DATE_FORMAT', 'M j, Y');
|
||||
define('DEFAULT_DATE_PICKER_FORMAT', 'M d, yyyy');
|
||||
define('DEFAULT_DATETIME_FORMAT', 'F j, Y g:i a');
|
||||
define('DEFAULT_DATETIME_MOMENT_FORMAT', 'MMM D, YYYY h:mm:ss a');
|
||||
define('DEFAULT_LOCALE', 'en');
|
||||
define('DEFAULT_MAP_ZOOM', 10);
|
||||
|
||||
define('RESULT_SUCCESS', 'success');
|
||||
define('RESULT_FAILURE', 'failure');
|
||||
|
||||
|
||||
define('PAYMENT_LIBRARY_OMNIPAY', 1);
|
||||
define('PAYMENT_LIBRARY_PHP_PAYMENTS', 2);
|
||||
|
||||
define('GATEWAY_AUTHORIZE_NET', 1);
|
||||
define('GATEWAY_EWAY', 4);
|
||||
define('GATEWAY_MOLLIE', 9);
|
||||
define('GATEWAY_PAYFAST', 13);
|
||||
define('GATEWAY_PAYPAL_EXPRESS', 17);
|
||||
define('GATEWAY_PAYPAL_PRO', 18);
|
||||
define('GATEWAY_SAGE_PAY_DIRECT', 20);
|
||||
define('GATEWAY_SAGE_PAY_SERVER', 21);
|
||||
define('GATEWAY_STRIPE', 23);
|
||||
define('GATEWAY_GOCARDLESS', 6);
|
||||
define('GATEWAY_TWO_CHECKOUT', 27);
|
||||
define('GATEWAY_BEANSTREAM', 29);
|
||||
define('GATEWAY_PSIGATE', 30);
|
||||
define('GATEWAY_MOOLAH', 31);
|
||||
define('GATEWAY_BITPAY', 42);
|
||||
define('GATEWAY_DWOLLA', 43);
|
||||
define('GATEWAY_CHECKOUT_COM', 47);
|
||||
define('GATEWAY_CYBERSOURCE', 49);
|
||||
define('GATEWAY_WEPAY', 60);
|
||||
define('GATEWAY_BRAINTREE', 61);
|
||||
define('GATEWAY_CUSTOM', 62);
|
||||
|
||||
// The customer exists, but only as a local concept
|
||||
// The remote gateway doesn't understand the concept of customers
|
||||
define('CUSTOMER_REFERENCE_LOCAL', 'local');
|
||||
|
||||
define('EVENT_CREATE_CLIENT', 1);
|
||||
define('EVENT_CREATE_INVOICE', 2);
|
||||
define('EVENT_CREATE_QUOTE', 3);
|
||||
define('EVENT_CREATE_PAYMENT', 4);
|
||||
define('EVENT_CREATE_VENDOR', 5);
|
||||
define('EVENT_UPDATE_QUOTE', 6);
|
||||
define('EVENT_DELETE_QUOTE', 7);
|
||||
define('EVENT_UPDATE_INVOICE', 8);
|
||||
define('EVENT_DELETE_INVOICE', 9);
|
||||
|
||||
define('REQUESTED_PRO_PLAN', 'REQUESTED_PRO_PLAN');
|
||||
define('DEMO_ACCOUNT_ID', 'DEMO_ACCOUNT_ID');
|
||||
define('PREV_USER_ID', 'PREV_USER_ID');
|
||||
define('NINJA_ACCOUNT_KEY', 'zg4ylmzDkdkPOT8yoKQw9LTWaoZJx79h');
|
||||
define('NINJA_GATEWAY_ID', GATEWAY_STRIPE);
|
||||
define('NINJA_GATEWAY_CONFIG', 'NINJA_GATEWAY_CONFIG');
|
||||
define('NINJA_WEB_URL', env('NINJA_WEB_URL', 'https://www.invoiceninja.com'));
|
||||
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', '3.0.0' . env('NINJA_VERSION_SUFFIX'));
|
||||
|
||||
define('SOCIAL_LINK_FACEBOOK', env('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'));
|
||||
define('SOCIAL_LINK_TWITTER', env('SOCIAL_LINK_TWITTER', 'https://twitter.com/invoiceninja'));
|
||||
define('SOCIAL_LINK_GITHUB', env('SOCIAL_LINK_GITHUB', 'https://github.com/invoiceninja/invoiceninja/'));
|
||||
|
||||
define('NINJA_FORUM_URL', env('NINJA_FORUM_URL', 'https://www.invoiceninja.com/forums/forum/support/'));
|
||||
define('NINJA_CONTACT_URL', env('NINJA_CONTACT_URL', 'https://www.invoiceninja.com/contact/'));
|
||||
define('NINJA_FROM_EMAIL', env('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com'));
|
||||
define('NINJA_IOS_APP_URL', 'https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=1072566815');
|
||||
define('RELEASES_URL', env('RELEASES_URL', 'https://trello.com/b/63BbiVVe/invoice-ninja'));
|
||||
define('ZAPIER_URL', env('ZAPIER_URL', 'https://zapier.com/zapbook/invoice-ninja'));
|
||||
define('OUTDATE_BROWSER_URL', env('OUTDATE_BROWSER_URL', 'http://browsehappy.com/'));
|
||||
define('PDFMAKE_DOCS', env('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html'));
|
||||
define('PHANTOMJS_CLOUD', env('PHANTOMJS_CLOUD', 'http://api.phantomjscloud.com/api/browser/v2/'));
|
||||
define('PHP_DATE_FORMATS', env('PHP_DATE_FORMATS', 'http://php.net/manual/en/function.date.php'));
|
||||
define('REFERRAL_PROGRAM_URL', env('REFERRAL_PROGRAM_URL', 'https://www.invoiceninja.com/referral-program/'));
|
||||
define('EMAIL_MARKUP_URL', env('EMAIL_MARKUP_URL', 'https://developers.google.com/gmail/markup'));
|
||||
define('OFX_HOME_URL', env('OFX_HOME_URL', 'http://www.ofxhome.com/index.php/home/directory/all'));
|
||||
define('GOOGLE_ANALYITCS_URL', env('GOOGLE_ANALYITCS_URL', 'https://www.google-analytics.com/collect'));
|
||||
define('TRANSIFEX_URL', env('TRANSIFEX_URL', 'https://www.transifex.com/invoice-ninja/invoice-ninja'));
|
||||
define('CHROME_PDF_HELP_URL', 'https://support.google.com/chrome/answer/6213030?hl=en');
|
||||
define('FIREFOX_PDF_HELP_URL', 'https://support.mozilla.org/en-US/kb/view-pdf-files-firefox');
|
||||
|
||||
define('MSBOT_LOGIN_URL', 'https://login.microsoftonline.com/common/oauth2/v2.0/token');
|
||||
define('MSBOT_LUIS_URL', 'https://api.projectoxford.ai/luis/v1/application');
|
||||
define('SKYPE_API_URL', 'https://apis.skype.com/v3');
|
||||
define('MSBOT_STATE_URL', 'https://state.botframework.com/v3');
|
||||
|
||||
define('BLANK_IMAGE', 'data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=');
|
||||
|
||||
define('COUNT_FREE_DESIGNS', 4);
|
||||
define('COUNT_FREE_DESIGNS_SELF_HOST', 5); // include the custom design
|
||||
define('PRODUCT_ONE_CLICK_INSTALL', 1);
|
||||
define('PRODUCT_INVOICE_DESIGNS', 2);
|
||||
define('PRODUCT_WHITE_LABEL', 3);
|
||||
define('PRODUCT_SELF_HOST', 4);
|
||||
define('WHITE_LABEL_AFFILIATE_KEY', '92D2J5');
|
||||
define('INVOICE_DESIGNS_AFFILIATE_KEY', 'T3RS74');
|
||||
define('SELF_HOST_AFFILIATE_KEY', '8S69AD');
|
||||
|
||||
define('PLAN_PRICE_PRO_MONTHLY', env('PLAN_PRICE_PRO_MONTHLY', 8));
|
||||
define('PLAN_PRICE_ENTERPRISE_MONTHLY_2', env('PLAN_PRICE_ENTERPRISE_MONTHLY_2', 12));
|
||||
define('PLAN_PRICE_ENTERPRISE_MONTHLY_5', env('PLAN_PRICE_ENTERPRISE_MONTHLY_5', 18));
|
||||
define('PLAN_PRICE_ENTERPRISE_MONTHLY_10', env('PLAN_PRICE_ENTERPRISE_MONTHLY_10', 24));
|
||||
define('PLAN_PRICE_ENTERPRISE_MONTHLY_20', env('PLAN_PRICE_ENTERPRISE_MONTHLY_20', 36));
|
||||
define('WHITE_LABEL_PRICE', env('WHITE_LABEL_PRICE', 20));
|
||||
define('INVOICE_DESIGNS_PRICE', env('INVOICE_DESIGNS_PRICE', 10));
|
||||
|
||||
define('USER_TYPE_SELF_HOST', 'SELF_HOST');
|
||||
define('USER_TYPE_CLOUD_HOST', 'CLOUD_HOST');
|
||||
define('NEW_VERSION_AVAILABLE', 'NEW_VERSION_AVAILABLE');
|
||||
|
||||
define('TEST_USERNAME', 'user@example.com');
|
||||
define('TEST_PASSWORD', 'password');
|
||||
define('API_SECRET', 'API_SECRET');
|
||||
define('DEFAULT_API_PAGE_SIZE', 15);
|
||||
define('MAX_API_PAGE_SIZE', 500);
|
||||
|
||||
define('IOS_DEVICE', env('IOS_DEVICE', ''));
|
||||
define('ANDROID_DEVICE', env('ANDROID_DEVICE', ''));
|
||||
|
||||
define('TOKEN_BILLING_DISABLED', 1);
|
||||
define('TOKEN_BILLING_OPT_IN', 2);
|
||||
define('TOKEN_BILLING_OPT_OUT', 3);
|
||||
define('TOKEN_BILLING_ALWAYS', 4);
|
||||
|
||||
define('PAYMENT_TYPE_CREDIT', 1);
|
||||
define('PAYMENT_TYPE_ACH', 5);
|
||||
define('PAYMENT_TYPE_VISA', 6);
|
||||
define('PAYMENT_TYPE_MASTERCARD', 7);
|
||||
define('PAYMENT_TYPE_AMERICAN_EXPRESS', 8);
|
||||
define('PAYMENT_TYPE_DISCOVER', 9);
|
||||
define('PAYMENT_TYPE_DINERS', 10);
|
||||
define('PAYMENT_TYPE_EUROCARD', 11);
|
||||
define('PAYMENT_TYPE_NOVA', 12);
|
||||
define('PAYMENT_TYPE_CREDIT_CARD_OTHER', 13);
|
||||
define('PAYMENT_TYPE_PAYPAL', 14);
|
||||
define('PAYMENT_TYPE_CARTE_BLANCHE', 17);
|
||||
define('PAYMENT_TYPE_UNIONPAY', 18);
|
||||
define('PAYMENT_TYPE_JCB', 19);
|
||||
define('PAYMENT_TYPE_LASER', 20);
|
||||
define('PAYMENT_TYPE_MAESTRO', 21);
|
||||
define('PAYMENT_TYPE_SOLO', 22);
|
||||
define('PAYMENT_TYPE_SWITCH', 23);
|
||||
|
||||
define('PAYMENT_METHOD_STATUS_NEW', 'new');
|
||||
define('PAYMENT_METHOD_STATUS_VERIFICATION_FAILED', 'verification_failed');
|
||||
define('PAYMENT_METHOD_STATUS_VERIFIED', 'verified');
|
||||
|
||||
define('GATEWAY_TYPE_CREDIT_CARD', 1);
|
||||
define('GATEWAY_TYPE_BANK_TRANSFER', 2);
|
||||
define('GATEWAY_TYPE_PAYPAL', 3);
|
||||
define('GATEWAY_TYPE_BITCOIN', 4);
|
||||
define('GATEWAY_TYPE_DWOLLA', 5);
|
||||
define('GATEWAY_TYPE_CUSTOM', 6);
|
||||
define('GATEWAY_TYPE_TOKEN', 'token');
|
||||
|
||||
define('REMINDER1', 'reminder1');
|
||||
define('REMINDER2', 'reminder2');
|
||||
define('REMINDER3', 'reminder3');
|
||||
|
||||
define('REMINDER_DIRECTION_AFTER', 1);
|
||||
define('REMINDER_DIRECTION_BEFORE', 2);
|
||||
|
||||
define('REMINDER_FIELD_DUE_DATE', 1);
|
||||
define('REMINDER_FIELD_INVOICE_DATE', 2);
|
||||
|
||||
define('FILTER_INVOICE_DATE', 'invoice_date');
|
||||
define('FILTER_PAYMENT_DATE', 'payment_date');
|
||||
|
||||
define('SOCIAL_GOOGLE', 'Google');
|
||||
define('SOCIAL_FACEBOOK', 'Facebook');
|
||||
define('SOCIAL_GITHUB', 'GitHub');
|
||||
define('SOCIAL_LINKEDIN', 'LinkedIn');
|
||||
|
||||
define('USER_STATE_ACTIVE', 'active');
|
||||
define('USER_STATE_PENDING', 'pending');
|
||||
define('USER_STATE_DISABLED', 'disabled');
|
||||
define('USER_STATE_ADMIN', 'admin');
|
||||
define('USER_STATE_OWNER', 'owner');
|
||||
|
||||
define('API_SERIALIZER_ARRAY', 'array');
|
||||
define('API_SERIALIZER_JSON', 'json');
|
||||
|
||||
define('EMAIL_DESIGN_PLAIN', 1);
|
||||
define('EMAIL_DESIGN_LIGHT', 2);
|
||||
define('EMAIL_DESIGN_DARK', 3);
|
||||
|
||||
define('BANK_LIBRARY_OFX', 1);
|
||||
|
||||
define('CURRENCY_DECORATOR_CODE', 'code');
|
||||
define('CURRENCY_DECORATOR_SYMBOL', 'symbol');
|
||||
define('CURRENCY_DECORATOR_NONE', 'none');
|
||||
|
||||
define('RESELLER_REVENUE_SHARE', 'A');
|
||||
define('RESELLER_LIMITED_USERS', 'B');
|
||||
|
||||
define('AUTO_BILL_OFF', 1);
|
||||
define('AUTO_BILL_OPT_IN', 2);
|
||||
define('AUTO_BILL_OPT_OUT', 3);
|
||||
define('AUTO_BILL_ALWAYS', 4);
|
||||
|
||||
// These must be lowercase
|
||||
define('PLAN_FREE', 'free');
|
||||
define('PLAN_PRO', 'pro');
|
||||
define('PLAN_ENTERPRISE', 'enterprise');
|
||||
define('PLAN_WHITE_LABEL', 'white_label');
|
||||
define('PLAN_TERM_MONTHLY', 'month');
|
||||
define('PLAN_TERM_YEARLY', 'year');
|
||||
|
||||
// Pro
|
||||
define('FEATURE_CUSTOMIZE_INVOICE_DESIGN', 'customize_invoice_design');
|
||||
define('FEATURE_REMOVE_CREATED_BY', 'remove_created_by');
|
||||
define('FEATURE_DIFFERENT_DESIGNS', 'different_designs');
|
||||
define('FEATURE_EMAIL_TEMPLATES_REMINDERS', 'email_templates_reminders');
|
||||
define('FEATURE_INVOICE_SETTINGS', 'invoice_settings');
|
||||
define('FEATURE_CUSTOM_EMAILS', 'custom_emails');
|
||||
define('FEATURE_PDF_ATTACHMENT', 'pdf_attachment');
|
||||
define('FEATURE_MORE_INVOICE_DESIGNS', 'more_invoice_designs');
|
||||
define('FEATURE_QUOTES', 'quotes');
|
||||
define('FEATURE_TASKS', 'tasks');
|
||||
define('FEATURE_EXPENSES', 'expenses');
|
||||
define('FEATURE_REPORTS', 'reports');
|
||||
define('FEATURE_BUY_NOW_BUTTONS', 'buy_now_buttons');
|
||||
define('FEATURE_API', 'api');
|
||||
define('FEATURE_CLIENT_PORTAL_PASSWORD', 'client_portal_password');
|
||||
define('FEATURE_CUSTOM_URL', 'custom_url');
|
||||
|
||||
define('FEATURE_MORE_CLIENTS', 'more_clients'); // No trial allowed
|
||||
|
||||
// Whitelabel
|
||||
define('FEATURE_WHITE_LABEL', 'feature_white_label');
|
||||
|
||||
// Enterprise
|
||||
define('FEATURE_DOCUMENTS', 'documents');
|
||||
|
||||
// No Trial allowed
|
||||
define('FEATURE_USERS', 'users');// Grandfathered for old Pro users
|
||||
define('FEATURE_USER_PERMISSIONS', 'user_permissions');
|
||||
|
||||
// Pro users who started paying on or before this date will be able to manage users
|
||||
define('PRO_USERS_GRANDFATHER_DEADLINE', '2016-06-04');
|
||||
define('EXTRAS_GRANDFATHER_COMPANY_ID', 35089);
|
||||
|
||||
// WePay
|
||||
define('WEPAY_PRODUCTION', 'production');
|
||||
define('WEPAY_STAGE', 'stage');
|
||||
define('WEPAY_CLIENT_ID', env('WEPAY_CLIENT_ID'));
|
||||
define('WEPAY_CLIENT_SECRET', env('WEPAY_CLIENT_SECRET'));
|
||||
define('WEPAY_AUTO_UPDATE', env('WEPAY_AUTO_UPDATE', false));
|
||||
define('WEPAY_ENVIRONMENT', env('WEPAY_ENVIRONMENT', WEPAY_PRODUCTION));
|
||||
define('WEPAY_ENABLE_CANADA', env('WEPAY_ENABLE_CANADA', false));
|
||||
define('WEPAY_THEME', env('WEPAY_THEME','{"name":"Invoice Ninja","primary_color":"0b4d78","secondary_color":"0b4d78","background_color":"f8f8f8","button_color":"33b753"}'));
|
||||
|
||||
define('SKYPE_CARD_RECEIPT', 'message/card.receipt');
|
||||
define('SKYPE_CARD_CAROUSEL', 'message/card.carousel');
|
||||
define('SKYPE_CARD_HERO', '');
|
||||
|
||||
define('BOT_STATE_GET_EMAIL', 'get_email');
|
||||
define('BOT_STATE_GET_CODE', 'get_code');
|
||||
define('BOT_STATE_READY', 'ready');
|
||||
define('SIMILAR_MIN_THRESHOLD', 50);
|
||||
|
||||
// https://docs.botframework.com/en-us/csharp/builder/sdkreference/attachments.html
|
||||
define('SKYPE_BUTTON_OPEN_URL', 'openUrl');
|
||||
define('SKYPE_BUTTON_IM_BACK', 'imBack');
|
||||
define('SKYPE_BUTTON_POST_BACK', 'postBack');
|
||||
define('SKYPE_BUTTON_CALL', 'call'); // "tel:123123123123"
|
||||
define('SKYPE_BUTTON_PLAY_AUDIO', 'playAudio');
|
||||
define('SKYPE_BUTTON_PLAY_VIDEO', 'playVideo');
|
||||
define('SKYPE_BUTTON_SHOW_IMAGE', 'showImage');
|
||||
define('SKYPE_BUTTON_DOWNLOAD_FILE', 'downloadFile');
|
||||
|
||||
define('INVOICE_FIELDS_CLIENT', 'client_fields');
|
||||
define('INVOICE_FIELDS_INVOICE', 'invoice_fields');
|
||||
define('INVOICE_FIELDS_ACCOUNT', 'account_fields');
|
||||
|
||||
$creditCards = [
|
||||
1 => ['card' => 'images/credit_cards/Test-Visa-Icon.png', 'text' => 'Visa'],
|
||||
2 => ['card' => 'images/credit_cards/Test-MasterCard-Icon.png', 'text' => 'Master Card'],
|
||||
4 => ['card' => 'images/credit_cards/Test-AmericanExpress-Icon.png', 'text' => 'American Express'],
|
||||
8 => ['card' => 'images/credit_cards/Test-Diners-Icon.png', 'text' => 'Diners'],
|
||||
16 => ['card' => 'images/credit_cards/Test-Discover-Icon.png', 'text' => 'Discover']
|
||||
];
|
||||
define('CREDIT_CARDS', serialize($creditCards));
|
||||
|
||||
$cachedTables = [
|
||||
'currencies' => 'App\Models\Currency',
|
||||
'sizes' => 'App\Models\Size',
|
||||
'industries' => 'App\Models\Industry',
|
||||
'timezones' => 'App\Models\Timezone',
|
||||
'dateFormats' => 'App\Models\DateFormat',
|
||||
'datetimeFormats' => 'App\Models\DatetimeFormat',
|
||||
'languages' => 'App\Models\Language',
|
||||
'paymentTerms' => 'App\Models\PaymentTerm',
|
||||
'paymentTypes' => 'App\Models\PaymentType',
|
||||
'countries' => 'App\Models\Country',
|
||||
'invoiceDesigns' => 'App\Models\InvoiceDesign',
|
||||
'invoiceStatus' => 'App\Models\InvoiceStatus',
|
||||
'frequencies' => 'App\Models\Frequency',
|
||||
'gateways' => 'App\Models\Gateway',
|
||||
'gatewayTypes' => 'App\Models\GatewayType',
|
||||
'fonts' => 'App\Models\Font',
|
||||
'banks' => 'App\Models\Bank',
|
||||
];
|
||||
define('CACHED_TABLES', serialize($cachedTables));
|
||||
|
||||
|
||||
// TODO remove these translation functions
|
||||
function uctrans($text)
|
||||
{
|
||||
return ucwords(trans($text));
|
||||
}
|
||||
|
||||
// optional trans: only return the string if it's translated
|
||||
function otrans($text)
|
||||
{
|
||||
$locale = Session::get(SESSION_LOCALE);
|
||||
|
||||
if ($locale == 'en') {
|
||||
return trans($text);
|
||||
} else {
|
||||
$string = trans($text);
|
||||
$english = trans($text, [], 'en');
|
||||
return $string != $english ? $string : '';
|
||||
}
|
||||
}
|
||||
|
||||
// include modules in translations
|
||||
function mtrans($entityType, $text = false)
|
||||
{
|
||||
if ( ! $text) {
|
||||
$text = $entityType;
|
||||
}
|
||||
|
||||
// check if this has been translated in a module language file
|
||||
if ( ! Utils::isNinjaProd() && $module = Module::find($entityType)) {
|
||||
$key = "{$module->getLowerName()}::texts.{$text}";
|
||||
$value = trans($key);
|
||||
if ($key != $value) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return trans("texts.{$text}");
|
||||
}
|
||||
}
|
29
app/Constants/Domain.php
Normal file
29
app/Constants/Domain.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php namespace App\Constants;
|
||||
|
||||
class Domain
|
||||
{
|
||||
const INVOICENINJA_COM = 1;
|
||||
const INVOICE_SERVICES = 2;
|
||||
|
||||
public static function getDomainFromId($id)
|
||||
{
|
||||
switch ($id) {
|
||||
case static::INVOICENINJA_COM:
|
||||
return 'invoiceninja.com';
|
||||
case static::INVOICE_SERVICES:
|
||||
return 'invoice.services';
|
||||
}
|
||||
|
||||
return 'invoiceninja.com';
|
||||
}
|
||||
|
||||
public static function getLinkFromId($id)
|
||||
{
|
||||
return 'https://app.' . static::getDomainFromId($id);
|
||||
}
|
||||
|
||||
public static function getEmailFromId($id)
|
||||
{
|
||||
return 'maildelivery@' . static::getDomainFromId($id);
|
||||
}
|
||||
}
|
@ -17,14 +17,20 @@ class InvoiceInvitationWasEmailed extends Event
|
||||
*/
|
||||
public $invitation;
|
||||
|
||||
/**
|
||||
* @var String
|
||||
*/
|
||||
public $notes;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param Invitation $invitation
|
||||
*/
|
||||
public function __construct(Invitation $invitation)
|
||||
public function __construct(Invitation $invitation, $notes)
|
||||
{
|
||||
$this->invitation = $invitation;
|
||||
$this->notes = $notes;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,14 +15,20 @@ class QuoteInvitationWasEmailed extends Event
|
||||
*/
|
||||
public $invitation;
|
||||
|
||||
/**
|
||||
* @var String
|
||||
*/
|
||||
public $notes;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param Invitation $invitation
|
||||
*/
|
||||
public function __construct(Invitation $invitation)
|
||||
public function __construct(Invitation $invitation, $notes)
|
||||
{
|
||||
$this->invitation = $invitation;
|
||||
$this->notes = $notes;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,11 +27,14 @@ class AccountApiController extends BaseAPIController
|
||||
$this->accountRepo = $accountRepo;
|
||||
}
|
||||
|
||||
public function ping()
|
||||
public function ping(Request $request)
|
||||
{
|
||||
$headers = Utils::getApiHeaders();
|
||||
|
||||
return Response::make(RESULT_SUCCESS, 200, $headers);
|
||||
if(hash_equals(env(API_SECRET),$request->api_secret))
|
||||
return Response::make(RESULT_SUCCESS, 200, $headers);
|
||||
else
|
||||
return $this->errorResponse(['message'=>'API Secret does not match .env variable'], 400);
|
||||
}
|
||||
|
||||
public function register(RegisterRequest $request)
|
||||
|
@ -39,6 +39,9 @@ use App\Services\AuthService;
|
||||
use App\Services\PaymentService;
|
||||
use App\Http\Requests\UpdateAccountRequest;
|
||||
|
||||
use App\Http\Requests\SaveClientPortalSettings;
|
||||
use App\Http\Requests\SaveEmailSettings;
|
||||
|
||||
/**
|
||||
* Class AccountController
|
||||
*/
|
||||
@ -233,6 +236,7 @@ class AccountController extends BaseController
|
||||
$company->plan_expires = date_create()->modify($term == PLAN_TERM_MONTHLY ? '+1 month' : '+1 year')->format('Y-m-d');
|
||||
}
|
||||
|
||||
$company->trial_plan = null;
|
||||
$company->plan = $plan;
|
||||
$company->save();
|
||||
|
||||
@ -521,7 +525,7 @@ class AccountController extends BaseController
|
||||
$data = [
|
||||
'account' => Auth::user()->account,
|
||||
'title' => trans('texts.tax_rates'),
|
||||
'taxRates' => TaxRate::scope()->get(['id', 'name', 'rate']),
|
||||
'taxRates' => TaxRate::scope()->whereIsInclusive(false)->get(['id', 'name', 'rate']),
|
||||
];
|
||||
|
||||
return View::make('accounts.tax_rates', $data);
|
||||
@ -555,12 +559,12 @@ class AccountController extends BaseController
|
||||
$document = new stdClass();
|
||||
|
||||
$client->name = 'Sample Client';
|
||||
$client->address1 = trans('texts.address1');
|
||||
$client->city = trans('texts.city');
|
||||
$client->state = trans('texts.state');
|
||||
$client->postal_code = trans('texts.postal_code');
|
||||
$client->work_phone = trans('texts.work_phone');
|
||||
$client->work_email = trans('texts.work_id');
|
||||
$client->address1 = '10 Main St.';
|
||||
$client->city = 'New York';
|
||||
$client->state = 'NY';
|
||||
$client->postal_code = '10000';
|
||||
$client->work_phone = '(212) 555-0000';
|
||||
$client->work_email = 'sample@example.com';
|
||||
|
||||
$invoice->invoice_number = '0000';
|
||||
$invoice->invoice_date = Utils::fromSqlDate(date('Y-m-d'));
|
||||
@ -713,14 +717,10 @@ class AccountController extends BaseController
|
||||
return AccountController::export();
|
||||
} elseif ($section === ACCOUNT_INVOICE_SETTINGS) {
|
||||
return AccountController::saveInvoiceSettings();
|
||||
} elseif ($section === ACCOUNT_EMAIL_SETTINGS) {
|
||||
return AccountController::saveEmailSettings();
|
||||
} elseif ($section === ACCOUNT_INVOICE_DESIGN) {
|
||||
return AccountController::saveInvoiceDesign();
|
||||
} elseif ($section === ACCOUNT_CUSTOMIZE_DESIGN) {
|
||||
return AccountController::saveCustomizeDesign();
|
||||
} elseif ($section === ACCOUNT_CLIENT_PORTAL) {
|
||||
return AccountController::saveClientPortal();
|
||||
} elseif ($section === ACCOUNT_TEMPLATES_AND_REMINDERS) {
|
||||
return AccountController::saveEmailTemplates();
|
||||
} elseif ($section === ACCOUNT_PRODUCTS) {
|
||||
@ -788,53 +788,30 @@ class AccountController extends BaseController
|
||||
/**
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
private function saveClientPortal()
|
||||
public function saveClientPortalSettings(SaveClientPortalSettings $request)
|
||||
{
|
||||
$account = Auth::user()->account;
|
||||
$account->fill(Input::all());
|
||||
|
||||
// Only allowed for pro Invoice Ninja users or white labeled self-hosted users
|
||||
if (Auth::user()->account->hasFeature(FEATURE_CLIENT_PORTAL_CSS)) {
|
||||
$input_css = Input::get('client_view_css');
|
||||
if (Utils::isNinja()) {
|
||||
// Allow referencing the body element
|
||||
$input_css = preg_replace('/(?<![a-z0-9\-\_\#\.])body(?![a-z0-9\-\_])/i', '.body', $input_css);
|
||||
|
||||
//
|
||||
// Inspired by http://stackoverflow.com/a/5209050/1721527, dleavitt <https://stackoverflow.com/users/362110/dleavitt>
|
||||
//
|
||||
|
||||
// Create a new configuration object
|
||||
$config = \HTMLPurifier_Config::createDefault();
|
||||
$config->set('Filter.ExtractStyleBlocks', true);
|
||||
$config->set('CSS.AllowImportant', true);
|
||||
$config->set('CSS.AllowTricky', true);
|
||||
$config->set('CSS.Trusted', true);
|
||||
|
||||
// Create a new purifier instance
|
||||
$purifier = new \HTMLPurifier($config);
|
||||
|
||||
// Wrap our CSS in style tags and pass to purifier.
|
||||
// we're not actually interested in the html response though
|
||||
$html = $purifier->purify('<style>'.$input_css.'</style>');
|
||||
|
||||
// The "style" blocks are stored seperately
|
||||
$output_css = $purifier->context->get('StyleBlocks');
|
||||
|
||||
// Get the first style block
|
||||
$sanitized_css = count($output_css) ? $output_css[0] : '';
|
||||
} else {
|
||||
$sanitized_css = $input_css;
|
||||
}
|
||||
|
||||
$account->client_view_css = $sanitized_css;
|
||||
}
|
||||
|
||||
$account = $request->user()->account;
|
||||
$account->fill($request->all());
|
||||
$account->subdomain = $request->subdomain;
|
||||
$account->iframe_url = $request->iframe_url;
|
||||
$account->save();
|
||||
|
||||
Session::flash('message', trans('texts.updated_settings'));
|
||||
return redirect('settings/' . ACCOUNT_CLIENT_PORTAL)
|
||||
->with('message', trans('texts.updated_settings'));
|
||||
}
|
||||
|
||||
return Redirect::to('settings/'.ACCOUNT_CLIENT_PORTAL);
|
||||
/**
|
||||
* @return $this|\Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function saveEmailSettings(SaveEmailSettings $request)
|
||||
{
|
||||
$account = $request->user()->account;
|
||||
$account->fill($request->all());
|
||||
$account->bcc_email = $request->bcc_email;
|
||||
$account->save();
|
||||
|
||||
return redirect('settings/' . ACCOUNT_EMAIL_SETTINGS)
|
||||
->with('message', trans('texts.updated_settings'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -904,83 +881,18 @@ class AccountController extends BaseController
|
||||
return Redirect::to('settings/'.ACCOUNT_PRODUCTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this|\Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
private function saveEmailSettings()
|
||||
{
|
||||
if (Auth::user()->account->hasFeature(FEATURE_CUSTOM_EMAILS)) {
|
||||
$user = Auth::user();
|
||||
$subdomain = null;
|
||||
$iframeURL = null;
|
||||
$rules = [];
|
||||
|
||||
if (Input::get('custom_link') == 'subdomain') {
|
||||
$subdomain = preg_replace('/[^a-zA-Z0-9_\-\.]/', '', substr(strtolower(Input::get('subdomain')), 0, MAX_SUBDOMAIN_LENGTH));
|
||||
if (Utils::isNinja()) {
|
||||
$exclude = [
|
||||
'www',
|
||||
'app',
|
||||
'mail',
|
||||
'admin',
|
||||
'blog',
|
||||
'user',
|
||||
'contact',
|
||||
'payment',
|
||||
'payments',
|
||||
'billing',
|
||||
'invoice',
|
||||
'business',
|
||||
'owner',
|
||||
'info',
|
||||
'ninja',
|
||||
'docs',
|
||||
'doc',
|
||||
'documents'
|
||||
];
|
||||
$rules['subdomain'] = "unique:accounts,subdomain,{$user->account_id},id|not_in:" . implode(',', $exclude);
|
||||
}
|
||||
} else {
|
||||
$iframeURL = preg_replace('/[^a-zA-Z0-9_\-\:\/\.]/', '', substr(strtolower(Input::get('iframe_url')), 0, MAX_IFRAME_URL_LENGTH));
|
||||
$iframeURL = rtrim($iframeURL, '/');
|
||||
}
|
||||
|
||||
$validator = Validator::make(Input::all(), $rules);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return Redirect::to('settings/'.ACCOUNT_EMAIL_SETTINGS)
|
||||
->withErrors($validator)
|
||||
->withInput();
|
||||
} else {
|
||||
$account = Auth::user()->account;
|
||||
$account->subdomain = $subdomain;
|
||||
$account->iframe_url = $iframeURL;
|
||||
$account->pdf_email_attachment = Input::get('pdf_email_attachment') ? true : false;
|
||||
$account->document_email_attachment = Input::get('document_email_attachment') ? true : false;
|
||||
$account->email_design_id = Input::get('email_design_id');
|
||||
|
||||
if (Utils::isNinja()) {
|
||||
$account->enable_email_markup = Input::get('enable_email_markup') ? true : false;
|
||||
}
|
||||
|
||||
$account->save();
|
||||
Session::flash('message', trans('texts.updated_settings'));
|
||||
}
|
||||
}
|
||||
|
||||
return Redirect::to('settings/'.ACCOUNT_EMAIL_SETTINGS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this|\Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
private function saveInvoiceSettings()
|
||||
{
|
||||
if (Auth::user()->account->hasFeature(FEATURE_INVOICE_SETTINGS)) {
|
||||
$rules = [
|
||||
'invoice_number_pattern' => 'has_counter',
|
||||
'quote_number_pattern' => 'has_counter',
|
||||
];
|
||||
$rules = [];
|
||||
foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_CLIENT] as $entityType) {
|
||||
if (Input::get("{$entityType}_number_type") == 'pattern') {
|
||||
$rules["{$entityType}_number_pattern"] = 'has_counter';
|
||||
}
|
||||
}
|
||||
|
||||
$validator = Validator::make(Input::all(), $rules);
|
||||
|
||||
@ -1015,6 +927,10 @@ class AccountController extends BaseController
|
||||
$account->auto_convert_quote = Input::get('auto_convert_quote');
|
||||
$account->recurring_invoice_number_prefix = Input::get('recurring_invoice_number_prefix');
|
||||
|
||||
$account->client_number_prefix = trim(Input::get('client_number_prefix'));
|
||||
$account->client_number_pattern = trim(Input::get('client_number_pattern'));
|
||||
$account->client_number_counter = Input::get('client_number_counter');
|
||||
|
||||
if (Input::has('recurring_hour')) {
|
||||
$account->recurring_hour = Input::get('recurring_hour');
|
||||
}
|
||||
@ -1023,20 +939,14 @@ class AccountController extends BaseController
|
||||
$account->quote_number_counter = Input::get('quote_number_counter');
|
||||
}
|
||||
|
||||
if (Input::get('invoice_number_type') == 'prefix') {
|
||||
$account->invoice_number_prefix = trim(Input::get('invoice_number_prefix'));
|
||||
$account->invoice_number_pattern = null;
|
||||
} else {
|
||||
$account->invoice_number_pattern = trim(Input::get('invoice_number_pattern'));
|
||||
$account->invoice_number_prefix = null;
|
||||
}
|
||||
|
||||
if (Input::get('quote_number_type') == 'prefix') {
|
||||
$account->quote_number_prefix = trim(Input::get('quote_number_prefix'));
|
||||
$account->quote_number_pattern = null;
|
||||
} else {
|
||||
$account->quote_number_pattern = trim(Input::get('quote_number_pattern'));
|
||||
$account->quote_number_prefix = null;
|
||||
foreach ([ENTITY_INVOICE, ENTITY_QUOTE, ENTITY_CLIENT] as $entityType) {
|
||||
if (Input::get("{$entityType}_number_type") == 'prefix') {
|
||||
$account->{"{$entityType}_number_prefix"} = trim(Input::get("{$entityType}_number_prefix"));
|
||||
$account->{"{$entityType}_number_pattern"} = null;
|
||||
} else {
|
||||
$account->{"{$entityType}_number_pattern"} = trim(Input::get("{$entityType}_number_pattern"));
|
||||
$account->{"{$entityType}_number_prefix"} = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$account->share_counter
|
||||
@ -1218,6 +1128,13 @@ class AccountController extends BaseController
|
||||
$user->email = trim(strtolower(Input::get('email')));
|
||||
$user->phone = trim(Input::get('phone'));
|
||||
|
||||
if ( ! Auth::user()->is_admin) {
|
||||
$user->notify_sent = Input::get('notify_sent');
|
||||
$user->notify_viewed = Input::get('notify_viewed');
|
||||
$user->notify_paid = Input::get('notify_paid');
|
||||
$user->notify_approved = Input::get('notify_approved');
|
||||
}
|
||||
|
||||
if (Utils::isNinja()) {
|
||||
if (Input::get('referral_code') && !$user->referral_code) {
|
||||
$user->referral_code = $this->accountRepo->getReferralCode();
|
||||
@ -1461,22 +1378,6 @@ class AccountController extends BaseController
|
||||
return Redirect::to('/settings/'.ACCOUNT_USER_DETAILS)->with('message', trans('texts.confirmation_resent'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $plan
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function startTrial($plan)
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
if ($user->isEligibleForTrial($plan)) {
|
||||
$user->account->startTrial($plan);
|
||||
}
|
||||
|
||||
return Redirect::back()->with('message', trans('texts.trial_success'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $section
|
||||
* @param bool $subSection
|
||||
|
@ -266,21 +266,21 @@ class AccountGatewayController extends BaseController
|
||||
$config->publishableKey = $oldConfig->publishableKey;
|
||||
}
|
||||
|
||||
$plaidClientId = Input::get('plaid_client_id');
|
||||
$plaidClientId = trim(Input::get('plaid_client_id'));
|
||||
if ($plaidClientId = str_replace('*', '', $plaidClientId)) {
|
||||
$config->plaidClientId = $plaidClientId;
|
||||
} elseif ($oldConfig && property_exists($oldConfig, 'plaidClientId')) {
|
||||
$config->plaidClientId = $oldConfig->plaidClientId;
|
||||
}
|
||||
|
||||
$plaidSecret = Input::get('plaid_secret');
|
||||
$plaidSecret = trim(Input::get('plaid_secret'));
|
||||
if ($plaidSecret = str_replace('*', '', $plaidSecret)) {
|
||||
$config->plaidSecret = $plaidSecret;
|
||||
} elseif ($oldConfig && property_exists($oldConfig, 'plaidSecret')) {
|
||||
$config->plaidSecret = $oldConfig->plaidSecret;
|
||||
}
|
||||
|
||||
$plaidPublicKey = Input::get('plaid_public_key');
|
||||
$plaidPublicKey = trim(Input::get('plaid_public_key'));
|
||||
if ($plaidPublicKey = str_replace('*', '', $plaidPublicKey)) {
|
||||
$config->plaidPublicKey = $plaidPublicKey;
|
||||
} elseif ($oldConfig && property_exists($oldConfig, 'plaidPublicKey')) {
|
||||
|
@ -56,6 +56,7 @@ class AppController extends BaseController
|
||||
$app = Input::get('app');
|
||||
$app['key'] = env('APP_KEY') ?: str_random(RANDOM_KEY_LENGTH);
|
||||
$app['debug'] = Input::get('debug') ? 'true' : 'false';
|
||||
$app['https'] = Input::get('https') ? 'true' : 'false';
|
||||
|
||||
$database = Input::get('database');
|
||||
$dbType = 'mysql'; // $database['default'];
|
||||
@ -80,6 +81,7 @@ class AppController extends BaseController
|
||||
|
||||
$_ENV['APP_ENV'] = 'production';
|
||||
$_ENV['APP_DEBUG'] = $app['debug'];
|
||||
$_ENV['REQUIRE_HTTPS'] = $app['https'];
|
||||
$_ENV['APP_URL'] = $app['url'];
|
||||
$_ENV['APP_KEY'] = $app['key'];
|
||||
$_ENV['APP_CIPHER'] = env('APP_CIPHER', 'AES-256-CBC');
|
||||
@ -157,6 +159,7 @@ class AppController extends BaseController
|
||||
|
||||
$_ENV['APP_URL'] = $app['url'];
|
||||
$_ENV['APP_DEBUG'] = Input::get('debug') ? 'true' : 'false';
|
||||
$_ENV['REQUIRE_HTTPS'] = Input::get('https') ? 'true' : 'false';
|
||||
|
||||
$_ENV['DB_TYPE'] = 'mysql'; // $db['default'];
|
||||
$_ENV['DB_HOST'] = $db['type']['host'];
|
||||
@ -279,7 +282,7 @@ class AppController extends BaseController
|
||||
// legacy fix: check cipher is in .env file
|
||||
if ( ! env('APP_CIPHER')) {
|
||||
$fp = fopen(base_path().'/.env', 'a');
|
||||
fwrite($fp, "\nAPP_CIPHER=rijndael-128");
|
||||
fwrite($fp, "\nAPP_CIPHER=AES-256-CBC");
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
|
@ -141,6 +141,7 @@ class AuthController extends Controller
|
||||
if (Auth::check()) {
|
||||
Event::fire(new UserLoggedIn());
|
||||
|
||||
/*
|
||||
$users = false;
|
||||
// we're linking a new account
|
||||
if ($request->link_accounts && $userId && Auth::user()->id != $userId) {
|
||||
@ -150,6 +151,9 @@ class AuthController extends Controller
|
||||
} else {
|
||||
$users = $this->accountRepo->loadAccounts(Auth::user()->id);
|
||||
}
|
||||
*/
|
||||
|
||||
$users = $this->accountRepo->loadAccounts(Auth::user()->id);
|
||||
Session::put(SESSION_USER_ACCOUNTS, $users);
|
||||
|
||||
} elseif ($user) {
|
||||
|
@ -61,9 +61,9 @@ class BlueVineController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
$account = $user->primaryAccount();
|
||||
$account->bluevine_status = 'signed_up';
|
||||
$account->save();
|
||||
$company = $user->account->company;
|
||||
$company->bluevine_status = 'signed_up';
|
||||
$company->save();
|
||||
|
||||
$quote_data = json_decode( $response->getBody() );
|
||||
|
||||
@ -74,9 +74,9 @@ class BlueVineController extends BaseController {
|
||||
$user = Auth::user();
|
||||
|
||||
if ( $user ) {
|
||||
$account = $user->primaryAccount();
|
||||
$account->bluevine_status = 'ignored';
|
||||
$account->save();
|
||||
$company = $user->account->company;
|
||||
$company->bluevine_status = 'ignored';
|
||||
$company->save();
|
||||
}
|
||||
|
||||
return 'success';
|
||||
|
@ -84,7 +84,10 @@ class ClientController extends BaseController
|
||||
$user = Auth::user();
|
||||
|
||||
$actionLinks = [];
|
||||
if($user->can('create', ENTITY_TASK)){
|
||||
if ($user->can('create', ENTITY_INVOICE)){
|
||||
$actionLinks[] = ['label' => trans('texts.new_invoice'), 'url' => URL::to('/invoices/create/'.$client->public_id)];
|
||||
}
|
||||
if ($user->can('create', ENTITY_TASK)){
|
||||
$actionLinks[] = ['label' => trans('texts.new_task'), 'url' => URL::to('/tasks/create/'.$client->public_id)];
|
||||
}
|
||||
if (Utils::hasFeature(FEATURE_QUOTES) && $user->can('create', ENTITY_QUOTE)) {
|
||||
@ -215,4 +218,28 @@ class ClientController extends BaseController
|
||||
|
||||
return $this->returnBulk(ENTITY_CLIENT, $action, $ids);
|
||||
}
|
||||
|
||||
public function statement()
|
||||
{
|
||||
$account = Auth::user()->account;
|
||||
$client = Client::scope(request()->client_id)->with('contacts')->firstOrFail();
|
||||
$invoice = $account->createInvoice(ENTITY_INVOICE);
|
||||
$invoice->client = $client;
|
||||
$invoice->date_format = $account->date_format ? $account->date_format->format_moment : 'MMM D, YYYY';
|
||||
$invoice->invoice_items = Invoice::scope()
|
||||
->with(['client'])
|
||||
->whereClientId($client->id)
|
||||
->invoices()
|
||||
->whereIsPublic(true)
|
||||
->where('balance', '>', 0)
|
||||
->get();
|
||||
|
||||
$data = [
|
||||
'showBreadcrumbs' => false,
|
||||
'client' => $client,
|
||||
'invoice' => $invoice,
|
||||
];
|
||||
|
||||
return view('clients.statement', $data);
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,8 @@ class ClientPortalController extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
$account->loadLocalizationSettings($client);
|
||||
|
||||
if (!Input::has('phantomjs') && !Input::has('silent') && !Session::has($invitationKey)
|
||||
&& (!Auth::check() || Auth::user()->account_id != $invoice->account_id)) {
|
||||
if ($invoice->isType(INVOICE_TYPE_QUOTE)) {
|
||||
@ -75,8 +77,6 @@ class ClientPortalController extends BaseController
|
||||
Session::put($invitationKey, true); // track this invitation has been seen
|
||||
Session::put('contact_key', $invitation->contact->contact_key);// track current contact
|
||||
|
||||
$account->loadLocalizationSettings($client);
|
||||
|
||||
$invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date);
|
||||
$invoice->due_date = Utils::fromSqlDate($invoice->due_date);
|
||||
$invoice->features = [
|
||||
@ -99,6 +99,11 @@ class ClientPortalController extends BaseController
|
||||
'phone',
|
||||
]);
|
||||
|
||||
// translate the client country name
|
||||
if ($invoice->client->country) {
|
||||
$invoice->client->country->name = trans('texts.country_' . $invoice->client->country->name);
|
||||
}
|
||||
|
||||
$data = [];
|
||||
$paymentTypes = $this->getPaymentTypes($account, $client, $invitation);
|
||||
$paymentURL = '';
|
||||
@ -204,9 +209,13 @@ class ClientPortalController extends BaseController
|
||||
return RESULT_FAILURE;
|
||||
}
|
||||
|
||||
$invitation->signature_base64 = Input::get('signature');
|
||||
$invitation->signature_date = date_create();
|
||||
$invitation->save();
|
||||
if ($signature = Input::get('signature')) {
|
||||
$invitation->signature_base64 = $signature;
|
||||
$invitation->signature_date = date_create();
|
||||
$invitation->save();
|
||||
}
|
||||
|
||||
session(['authorized:' . $invitation->invitation_key => true]);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
@ -43,11 +43,13 @@ class DashboardController extends BaseController
|
||||
$expenses = $dashboardRepo->expenses($accountId, $userId, $viewAll);
|
||||
$tasks = $dashboardRepo->tasks($accountId, $userId, $viewAll);
|
||||
|
||||
$showBlueVinePromo = $user->is_admin
|
||||
$showBlueVinePromo = $user->is_admin
|
||||
&& env('BLUEVINE_PARTNER_UNIQUE_ID')
|
||||
&& ! $account->company->bluevine_status
|
||||
&& $account->created_at <= date( 'Y-m-d', strtotime( '-1 month' ));
|
||||
|
||||
$showWhiteLabelExpired = Utils::isSelfHost() && $account->company->hasExpiredPlan(PLAN_WHITE_LABEL);
|
||||
|
||||
// check if the account has quotes
|
||||
$hasQuotes = false;
|
||||
foreach ([$upcoming, $pastDue] as $data) {
|
||||
@ -97,6 +99,7 @@ class DashboardController extends BaseController
|
||||
'expenses' => $expenses,
|
||||
'tasks' => $tasks,
|
||||
'showBlueVinePromo' => $showBlueVinePromo,
|
||||
'showWhiteLabelExpired' => $showWhiteLabelExpired,
|
||||
];
|
||||
|
||||
if ($showBlueVinePromo) {
|
||||
|
@ -33,7 +33,20 @@ class DocumentAPIController extends BaseAPIController
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Http\Response
|
||||
* @SWG\Get(
|
||||
* path="/documents",
|
||||
* summary="List of document",
|
||||
* tags={"document"},
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="A list with documents",
|
||||
* @SWG\Schema(type="array", @SWG\Items(ref="#/definitions/Document"))
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response="default",
|
||||
* description="an ""unexpected"" error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
@ -59,13 +72,29 @@ class DocumentAPIController extends BaseAPIController
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CreateDocumentRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
* @SWG\Post(
|
||||
* path="/documents",
|
||||
* tags={"document"},
|
||||
* summary="Create a document",
|
||||
* @SWG\Parameter(
|
||||
* in="body",
|
||||
* name="body",
|
||||
* @SWG\Schema(ref="#/definitions/Document")
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="New document",
|
||||
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Document"))
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response="default",
|
||||
* description="an ""unexpected"" error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function store(CreateDocumentRequest $request)
|
||||
{
|
||||
|
||||
|
||||
$document = $this->documentRepo->upload($request->all());
|
||||
|
||||
return $this->itemResponse($document);
|
||||
|
@ -9,7 +9,6 @@ use App\Http\Requests\CreateExpenseCategoryRequest;
|
||||
use App\Http\Requests\UpdateExpenseCategoryRequest;
|
||||
use App\Ninja\Repositories\ExpenseCategoryRepository;
|
||||
|
||||
|
||||
class ExpenseCategoryApiController extends BaseAPIController
|
||||
{
|
||||
protected $categoryRepo;
|
||||
@ -19,23 +18,65 @@ class ExpenseCategoryApiController extends BaseAPIController
|
||||
public function __construct(ExpenseCategoryRepository $categoryRepo, ExpenseCategoryService $categoryService)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
|
||||
$this->categoryRepo = $categoryRepo;
|
||||
$this->categoryService = $categoryService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @SWG\Post(
|
||||
* path="/expense_categories",
|
||||
* tags={"expense_category"},
|
||||
* summary="Create an expense category",
|
||||
* @SWG\Parameter(
|
||||
* in="body",
|
||||
* name="body",
|
||||
* @SWG\Schema(ref="#/definitions/ExpenseCategory")
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="New expense category",
|
||||
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/ExpenseCategory"))
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response="default",
|
||||
* description="an ""unexpected"" error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function store(CreateExpenseCategoryRequest $request)
|
||||
{
|
||||
$category = $this->categoryRepo->save($request->input());
|
||||
|
||||
return $this->itemResponse($category);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @SWG\Put(
|
||||
* path="/expense_categories/{expense_category_id}",
|
||||
* tags={"expense_category"},
|
||||
* summary="Update an expense category",
|
||||
* @SWG\Parameter(
|
||||
* in="body",
|
||||
* name="body",
|
||||
* @SWG\Schema(ref="#/definitions/ExpenseCategory")
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="Update expense category",
|
||||
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/ExpenseCategory"))
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response="default",
|
||||
* description="an ""unexpected"" error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function update(UpdateExpenseCategoryRequest $request)
|
||||
{
|
||||
$category = $this->categoryRepo->save($request->input(), $request->entity());
|
||||
|
||||
return $this->itemResponse($category);
|
||||
}
|
||||
|
||||
public function store(CreateExpenseCategoryRequest $request)
|
||||
{
|
||||
$category = $this->categoryRepo->save($request->input());
|
||||
|
||||
return $this->itemResponse($category);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ class ExpenseCategoryController extends BaseController
|
||||
|
||||
Session::flash('message', trans('texts.created_expense_category'));
|
||||
|
||||
return redirect()->to('/expense_categories');
|
||||
return redirect()->to($category->getRoute());
|
||||
}
|
||||
|
||||
public function update(UpdateExpenseCategoryRequest $request)
|
||||
|
@ -253,7 +253,7 @@ class ExpenseController extends BaseController
|
||||
'customLabel1' => Auth::user()->account->custom_vendor_label1,
|
||||
'customLabel2' => Auth::user()->account->custom_vendor_label2,
|
||||
'categories' => ExpenseCategory::whereAccountId(Auth::user()->account_id)->withArchived()->orderBy('name')->get(),
|
||||
'taxRates' => TaxRate::scope()->orderBy('name')->get(),
|
||||
'taxRates' => TaxRate::scope()->whereIsInclusive(false)->orderBy('name')->get(),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,21 @@ class ExportController extends BaseController
|
||||
{
|
||||
$format = $request->input('format');
|
||||
$date = date('Y-m-d');
|
||||
$fileName = "invoice-ninja-{$date}";
|
||||
|
||||
// set the filename based on the entity types selected
|
||||
if ($request->include == 'all') {
|
||||
$fileName = "invoice-ninja-{$date}";
|
||||
} else {
|
||||
$fields = $request->all();
|
||||
$fields = array_filter(array_map(function ($key) {
|
||||
if ( ! in_array($key, ['format', 'include', '_token'])) {
|
||||
return $key;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}, array_keys($fields), $fields));
|
||||
$fileName = "invoice-ninja-" . join('-', $fields) . "-{$date}";
|
||||
}
|
||||
|
||||
if ($format === 'JSON') {
|
||||
return $this->returnJSON($request, $fileName);
|
||||
|
@ -1,10 +1,12 @@
|
||||
<?php namespace App\Http\Controllers;
|
||||
|
||||
use Response;
|
||||
use Request;
|
||||
use Redirect;
|
||||
use Auth;
|
||||
use View;
|
||||
use Input;
|
||||
use Mail;
|
||||
use Session;
|
||||
use App\Models\Account;
|
||||
use App\Libraries\Utils;
|
||||
@ -48,14 +50,6 @@ class HomeController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function showTerms()
|
||||
{
|
||||
return View::make('public.terms', ['hideHeader' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
@ -134,4 +128,24 @@ class HomeController extends BaseController
|
||||
{
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function contactUs()
|
||||
{
|
||||
Mail::raw(request()->message, function ($message) {
|
||||
$subject = 'Customer Message';
|
||||
if ( ! Utils::isNinja()) {
|
||||
$subject .= ': v' . NINJA_VERSION;
|
||||
}
|
||||
$message->to(CONTACT_EMAIL)
|
||||
->from(CONTACT_EMAIL, Auth::user()->present()->fullName)
|
||||
->replyTo(Auth::user()->email, Auth::user()->present()->fullName)
|
||||
->subject($subject);
|
||||
});
|
||||
|
||||
return redirect(Request::server('HTTP_REFERER'))
|
||||
->with('message', trans('texts.contact_us_response'));
|
||||
}
|
||||
}
|
||||
|
@ -12,12 +12,13 @@ use App\Models\Product;
|
||||
use App\Ninja\Repositories\ClientRepository;
|
||||
use App\Ninja\Repositories\PaymentRepository;
|
||||
use App\Ninja\Repositories\InvoiceRepository;
|
||||
use App\Ninja\Mailers\ContactMailer as Mailer;
|
||||
use App\Http\Requests\InvoiceRequest;
|
||||
use App\Http\Requests\CreateInvoiceAPIRequest;
|
||||
use App\Http\Requests\UpdateInvoiceAPIRequest;
|
||||
use App\Services\InvoiceService;
|
||||
use App\Services\PaymentService;
|
||||
use App\Jobs\SendInvoiceEmail;
|
||||
use App\Jobs\SendPaymentEmail;
|
||||
|
||||
class InvoiceApiController extends BaseAPIController
|
||||
{
|
||||
@ -25,7 +26,7 @@ class InvoiceApiController extends BaseAPIController
|
||||
|
||||
protected $entityType = ENTITY_INVOICE;
|
||||
|
||||
public function __construct(InvoiceService $invoiceService, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, PaymentRepository $paymentRepo, Mailer $mailer, PaymentService $paymentService)
|
||||
public function __construct(InvoiceService $invoiceService, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, PaymentRepository $paymentRepo, PaymentService $paymentService)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
@ -33,7 +34,6 @@ class InvoiceApiController extends BaseAPIController
|
||||
$this->clientRepo = $clientRepo;
|
||||
$this->paymentRepo = $paymentRepo;
|
||||
$this->invoiceService = $invoiceService;
|
||||
$this->mailer = $mailer;
|
||||
$this->paymentService = $paymentService;
|
||||
}
|
||||
|
||||
@ -185,9 +185,9 @@ class InvoiceApiController extends BaseAPIController
|
||||
|
||||
if ($isEmailInvoice) {
|
||||
if ($payment) {
|
||||
$this->mailer->sendPaymentConfirmation($payment);
|
||||
$this->dispatch(new SendPaymentEmail($payment));
|
||||
} elseif ( ! $invoice->is_recurring) {
|
||||
$this->mailer->sendInvoice($invoice);
|
||||
$this->dispatch(new SendInvoiceEmail($invoice));
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,7 +229,7 @@ class InvoiceApiController extends BaseAPIController
|
||||
}
|
||||
|
||||
if (!isset($data['invoice_date'])) {
|
||||
$fields['invoice_date_sql'] = date_create()->format('Y-m-d');
|
||||
$fields['invoice_date_sql'] = Utils::today();
|
||||
}
|
||||
if (!isset($data['due_date'])) {
|
||||
$fields['due_date_sql'] = false;
|
||||
@ -293,7 +293,7 @@ class InvoiceApiController extends BaseAPIController
|
||||
{
|
||||
$invoice = $request->entity();
|
||||
|
||||
$this->mailer->sendInvoice($invoice);
|
||||
$this->dispatch(new SendInvoiceEmail($invoice));
|
||||
|
||||
$response = json_encode(RESULT_SUCCESS, JSON_PRETTY_PRINT);
|
||||
$headers = Utils::getApiHeaders();
|
||||
|
@ -9,16 +9,16 @@ use Cache;
|
||||
use Redirect;
|
||||
use DB;
|
||||
use URL;
|
||||
use DropdownButton;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Client;
|
||||
use App\Models\Account;
|
||||
use App\Models\Product;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Payment;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\InvoiceDesign;
|
||||
use App\Models\Activity;
|
||||
use App\Ninja\Mailers\ContactMailer as Mailer;
|
||||
use App\Jobs\SendInvoiceEmail;
|
||||
use App\Ninja\Repositories\InvoiceRepository;
|
||||
use App\Ninja\Repositories\ClientRepository;
|
||||
use App\Ninja\Repositories\DocumentRepository;
|
||||
@ -32,7 +32,6 @@ use App\Http\Requests\UpdateInvoiceRequest;
|
||||
|
||||
class InvoiceController extends BaseController
|
||||
{
|
||||
protected $mailer;
|
||||
protected $invoiceRepo;
|
||||
protected $clientRepo;
|
||||
protected $documentRepo;
|
||||
@ -41,11 +40,10 @@ class InvoiceController extends BaseController
|
||||
protected $recurringInvoiceService;
|
||||
protected $entityType = ENTITY_INVOICE;
|
||||
|
||||
public function __construct(Mailer $mailer, InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService, DocumentRepository $documentRepo, RecurringInvoiceService $recurringInvoiceService, PaymentService $paymentService)
|
||||
public function __construct(InvoiceRepository $invoiceRepo, ClientRepository $clientRepo, InvoiceService $invoiceService, DocumentRepository $documentRepo, RecurringInvoiceService $recurringInvoiceService, PaymentService $paymentService)
|
||||
{
|
||||
// parent::__construct();
|
||||
|
||||
$this->mailer = $mailer;
|
||||
$this->invoiceRepo = $invoiceRepo;
|
||||
$this->clientRepo = $clientRepo;
|
||||
$this->invoiceService = $invoiceService;
|
||||
@ -100,10 +98,10 @@ class InvoiceController extends BaseController
|
||||
if ($clone) {
|
||||
$invoice->id = $invoice->public_id = null;
|
||||
$invoice->is_public = false;
|
||||
$invoice->invoice_number = $account->getNextInvoiceNumber($invoice);
|
||||
$invoice->invoice_number = $account->getNextNumber($invoice);
|
||||
$invoice->balance = $invoice->amount;
|
||||
$invoice->invoice_status_id = 0;
|
||||
$invoice->invoice_date = date_create()->format('Y-m-d');
|
||||
$invoice->invoice_date = Utils::today();
|
||||
$method = 'POST';
|
||||
$url = "{$entityType}s";
|
||||
} else {
|
||||
@ -124,44 +122,6 @@ class InvoiceController extends BaseController
|
||||
'invoice_settings' => Auth::user()->hasFeature(FEATURE_INVOICE_SETTINGS),
|
||||
];
|
||||
|
||||
$actions = [
|
||||
['url' => 'javascript:onCloneClick()', 'label' => trans("texts.clone_{$entityType}")],
|
||||
['url' => URL::to("{$entityType}s/{$entityType}_history/{$invoice->public_id}"), 'label' => trans('texts.view_history')],
|
||||
DropdownButton::DIVIDER
|
||||
];
|
||||
|
||||
if ($entityType == ENTITY_QUOTE) {
|
||||
if ($invoice->quote_invoice_id) {
|
||||
$actions[] = ['url' => URL::to("invoices/{$invoice->quote_invoice_id}/edit"), 'label' => trans('texts.view_invoice')];
|
||||
} else {
|
||||
$actions[] = ['url' => 'javascript:onConvertClick()', 'label' => trans('texts.convert_to_invoice')];
|
||||
}
|
||||
} elseif ($entityType == ENTITY_INVOICE) {
|
||||
if ($invoice->quote_id) {
|
||||
$actions[] = ['url' => URL::to("quotes/{$invoice->quote_id}/edit"), 'label' => trans('texts.view_quote')];
|
||||
}
|
||||
|
||||
if (!$invoice->is_recurring && $invoice->balance > 0 && $invoice->is_public) {
|
||||
$actions[] = ['url' => 'javascript:submitBulkAction("markPaid")', 'label' => trans('texts.mark_paid')];
|
||||
$actions[] = ['url' => 'javascript:onPaymentClick()', 'label' => trans('texts.enter_payment')];
|
||||
}
|
||||
|
||||
foreach ($invoice->payments as $payment) {
|
||||
$label = trans('texts.view_payment');
|
||||
if (count($invoice->payments) > 1) {
|
||||
$label .= ' - ' . $account->formatMoney($payment->amount, $invoice->client);
|
||||
}
|
||||
$actions[] = ['url' => $payment->present()->url, 'label' => $label];
|
||||
}
|
||||
}
|
||||
|
||||
if (count($actions) > 3) {
|
||||
$actions[] = DropdownButton::DIVIDER;
|
||||
}
|
||||
|
||||
$actions[] = ['url' => 'javascript:onArchiveClick()', 'label' => trans("texts.archive_{$entityType}")];
|
||||
$actions[] = ['url' => 'javascript:onDeleteClick()', 'label' => trans("texts.delete_{$entityType}")];
|
||||
|
||||
$lastSent = ($invoice->is_recurring && $invoice->last_sent_date) ? $invoice->recurring_invoices->last() : null;
|
||||
|
||||
if(!Auth::user()->hasPermission('view_all')){
|
||||
@ -179,7 +139,6 @@ class InvoiceController extends BaseController
|
||||
'title' => trans("texts.edit_{$entityType}"),
|
||||
'client' => $invoice->client,
|
||||
'isRecurring' => $invoice->is_recurring,
|
||||
'actions' => $actions,
|
||||
'lastSent' => $lastSent];
|
||||
$data = array_merge($data, self::getViewModel($invoice));
|
||||
|
||||
@ -326,7 +285,11 @@ class InvoiceController extends BaseController
|
||||
$defaultTax = false;
|
||||
|
||||
foreach ($rates as $rate) {
|
||||
$options[$rate->rate . ' ' . $rate->name] = $rate->name . ' ' . ($rate->rate+0) . '%';
|
||||
$name = $rate->name . ' ' . ($rate->rate+0) . '%';
|
||||
if ($rate->is_inclusive) {
|
||||
$name .= ' - ' . trans('texts.inclusive');
|
||||
}
|
||||
$options[($rate->is_inclusive ? '1 ' : '0 ') . $rate->rate . ' ' . $rate->name] = $name;
|
||||
|
||||
// load default invoice tax
|
||||
if ($rate->id == $account->default_tax_rate_id) {
|
||||
@ -340,7 +303,7 @@ class InvoiceController extends BaseController
|
||||
if (isset($options[$key])) {
|
||||
continue;
|
||||
}
|
||||
$options[$key] = $rate['name'] . ' ' . $rate['rate'] . '%';
|
||||
$options['0 ' . $key] = $rate['name'] . ' ' . $rate['rate'] . '%';
|
||||
}
|
||||
}
|
||||
|
||||
@ -452,7 +415,8 @@ class InvoiceController extends BaseController
|
||||
if ($invoice->is_recurring) {
|
||||
$response = $this->emailRecurringInvoice($invoice);
|
||||
} else {
|
||||
$response = $this->mailer->sendInvoice($invoice, false, $pdfUpload);
|
||||
$this->dispatch(new SendInvoiceEmail($invoice, false, $pdfUpload));
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($response === true) {
|
||||
@ -482,7 +446,8 @@ class InvoiceController extends BaseController
|
||||
if ($invoice->isPaid()) {
|
||||
return true;
|
||||
} else {
|
||||
return $this->mailer->sendInvoice($invoice);
|
||||
$this->dispatch(new SendInvoiceEmail($invoice));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -514,6 +479,8 @@ class InvoiceController extends BaseController
|
||||
if ($count > 0) {
|
||||
if ($action == 'markSent') {
|
||||
$key = 'marked_sent_invoice';
|
||||
} elseif ($action == 'emailInvoice') {
|
||||
$key = 'emailed_' . $entityType;
|
||||
} elseif ($action == 'markPaid') {
|
||||
$key = 'created_payment';
|
||||
} else {
|
||||
@ -543,6 +510,8 @@ class InvoiceController extends BaseController
|
||||
public function invoiceHistory(InvoiceRequest $request)
|
||||
{
|
||||
$invoice = $request->entity();
|
||||
$paymentId = $request->payment_id ? Payment::getPrivateId($request->payment_id) : false;
|
||||
|
||||
$invoice->load('user', 'invoice_items', 'documents', 'expenses', 'expenses.documents', 'account.country', 'client.contacts', 'client.country');
|
||||
$invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date);
|
||||
$invoice->due_date = Utils::fromSqlDate($invoice->due_date);
|
||||
@ -553,17 +522,21 @@ class InvoiceController extends BaseController
|
||||
];
|
||||
$invoice->invoice_type_id = intval($invoice->invoice_type_id);
|
||||
|
||||
$activityTypeId = $invoice->isType(INVOICE_TYPE_QUOTE) ? ACTIVITY_TYPE_UPDATE_QUOTE : ACTIVITY_TYPE_UPDATE_INVOICE;
|
||||
$activities = Activity::scope(false, $invoice->account_id)
|
||||
->where('activity_type_id', '=', $activityTypeId)
|
||||
->where('invoice_id', '=', $invoice->id)
|
||||
->orderBy('id', 'desc')
|
||||
->get(['id', 'created_at', 'user_id', 'json_backup']);
|
||||
$activities = Activity::scope(false, $invoice->account_id);
|
||||
if ($paymentId) {
|
||||
$activities->whereIn('activity_type_id', [ACTIVITY_TYPE_CREATE_PAYMENT])
|
||||
->where('payment_id', '=', $paymentId);
|
||||
} else {
|
||||
$activities->whereIn('activity_type_id', [ACTIVITY_TYPE_UPDATE_INVOICE, ACTIVITY_TYPE_UPDATE_QUOTE])
|
||||
->where('invoice_id', '=', $invoice->id);
|
||||
}
|
||||
$activities = $activities->orderBy('id', 'desc')
|
||||
->get(['id', 'created_at', 'user_id', 'json_backup', 'activity_type_id', 'payment_id']);
|
||||
|
||||
$versionsJson = [];
|
||||
$versionsSelect = [];
|
||||
$lastId = false;
|
||||
|
||||
//dd($activities->toArray());
|
||||
foreach ($activities as $activity) {
|
||||
if ($backup = json_decode($activity->json_backup)) {
|
||||
$backup->invoice_date = Utils::fromSqlDate($backup->invoice_date);
|
||||
@ -576,16 +549,17 @@ class InvoiceController extends BaseController
|
||||
$backup->invoice_type_id = isset($backup->invoice_type_id) && intval($backup->invoice_type_id) == INVOICE_TYPE_QUOTE;
|
||||
$backup->account = $invoice->account->toArray();
|
||||
|
||||
$versionsJson[$activity->id] = $backup;
|
||||
$versionsJson[$paymentId ? 0 : $activity->id] = $backup;
|
||||
$key = Utils::timestampToDateTimeString(strtotime($activity->created_at)) . ' - ' . $activity->user->getDisplayName();
|
||||
$versionsSelect[$lastId ? $lastId : 0] = $key;
|
||||
$versionsSelect[$lastId ?: 0] = $key;
|
||||
$lastId = $activity->id;
|
||||
} else {
|
||||
Utils::logError('Failed to parse invoice backup');
|
||||
}
|
||||
}
|
||||
|
||||
if ($lastId) {
|
||||
// Show the current version as the last in the history
|
||||
if ( ! $paymentId) {
|
||||
$versionsSelect[$lastId] = Utils::timestampToDateTimeString(strtotime($invoice->created_at)) . ' - ' . $invoice->user->getDisplayName();
|
||||
}
|
||||
|
||||
@ -595,6 +569,7 @@ class InvoiceController extends BaseController
|
||||
'versionsSelect' => $versionsSelect,
|
||||
'invoiceDesigns' => InvoiceDesign::getDesigns(),
|
||||
'invoiceFonts' => Cache::get('fonts'),
|
||||
'paymentId' => $paymentId,
|
||||
];
|
||||
|
||||
return View::make('invoices.history', $data);
|
||||
|
@ -6,6 +6,7 @@ use Input;
|
||||
use Utils;
|
||||
use View;
|
||||
use Validator;
|
||||
use Auth;
|
||||
use URL;
|
||||
use Cache;
|
||||
use Omnipay;
|
||||
@ -274,4 +275,15 @@ class NinjaController extends BaseController
|
||||
Session::flash('error', $message);
|
||||
Utils::logError("Payment Error [{$type}]: " . ($exception ? Utils::getErrorString($exception) : $message), 'PHP', true);
|
||||
}
|
||||
|
||||
public function hideWhiteLabelMessage()
|
||||
{
|
||||
$user = Auth::user();
|
||||
$company = $user->account->company;
|
||||
|
||||
$company->plan = null;
|
||||
$company->save();
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ use Exception;
|
||||
use Validator;
|
||||
use App\Models\Invitation;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Product;
|
||||
use App\Models\PaymentMethod;
|
||||
@ -76,6 +77,11 @@ class OnlinePaymentController extends BaseController
|
||||
|
||||
$invitation = $invitation->load('invoice.client.account.account_gateways.gateway');
|
||||
$account = $invitation->account;
|
||||
|
||||
if ($account->requiresAuthorization($invitation->invoice) && ! session('authorized:' . $invitation->invitation_key)) {
|
||||
return redirect()->to('view/' . $invitation->invitation_key);
|
||||
}
|
||||
|
||||
$account->loadLocalizationSettings($invitation->invoice->client);
|
||||
|
||||
if ( ! $gatewayTypeAlias) {
|
||||
@ -283,22 +289,31 @@ class OnlinePaymentController extends BaseController
|
||||
return redirect()->to("{$failureUrl}/?error=invalid product");
|
||||
}
|
||||
|
||||
$rules = [
|
||||
'first_name' => 'string|max:100',
|
||||
'last_name' => 'string|max:100',
|
||||
'email' => 'email|string|max:100',
|
||||
];
|
||||
|
||||
$validator = Validator::make(Input::all(), $rules);
|
||||
if ($validator->fails()) {
|
||||
return redirect()->to("{$failureUrl}/?error=" . $validator->errors()->first());
|
||||
// check for existing client using contact_key
|
||||
$client = false;
|
||||
if ($contactKey = Input::get('contact_key')) {
|
||||
$client = Client::scope()->whereHas('contacts', function ($query) use ($contactKey) {
|
||||
$query->where('contact_key', $contactKey);
|
||||
})->first();
|
||||
}
|
||||
if ( ! $client) {
|
||||
$rules = [
|
||||
'first_name' => 'string|max:100',
|
||||
'last_name' => 'string|max:100',
|
||||
'email' => 'email|string|max:100',
|
||||
];
|
||||
|
||||
$data = [
|
||||
'currency_id' => $account->currency_id,
|
||||
'contact' => Input::all()
|
||||
];
|
||||
$client = $clientRepo->save($data);
|
||||
$validator = Validator::make(Input::all(), $rules);
|
||||
if ($validator->fails()) {
|
||||
return redirect()->to("{$failureUrl}/?error=" . $validator->errors()->first());
|
||||
}
|
||||
|
||||
$data = [
|
||||
'currency_id' => $account->currency_id,
|
||||
'contact' => Input::all()
|
||||
];
|
||||
$client = $clientRepo->save($data);
|
||||
}
|
||||
|
||||
$data = [
|
||||
'client_id' => $client->id,
|
||||
|
@ -108,6 +108,9 @@ class PaymentApiController extends BaseAPIController
|
||||
*/
|
||||
public function store(CreatePaymentAPIRequest $request)
|
||||
{
|
||||
// check payment has been marked sent
|
||||
$request->invoice->markSentIfUnsent();
|
||||
|
||||
$payment = $this->paymentRepo->save($request->input());
|
||||
|
||||
if (Input::get('email_receipt')) {
|
||||
@ -142,7 +145,7 @@ class PaymentApiController extends BaseAPIController
|
||||
public function destroy(UpdatePaymentRequest $request)
|
||||
{
|
||||
$payment = $request->entity();
|
||||
|
||||
|
||||
$this->clientRepo->delete($payment);
|
||||
|
||||
return $this->itemResponse($payment);
|
||||
|
@ -5,6 +5,7 @@ use Session;
|
||||
use Utils;
|
||||
use View;
|
||||
use Cache;
|
||||
use DropdownButton;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Client;
|
||||
use App\Ninja\Repositories\PaymentRepository;
|
||||
@ -84,7 +85,6 @@ class PaymentController extends BaseController
|
||||
{
|
||||
$invoices = Invoice::scope()
|
||||
->invoices()
|
||||
->whereIsPublic(true)
|
||||
->where('invoices.balance', '>', 0)
|
||||
->with('client', 'invoice_status')
|
||||
->orderBy('invoice_number')->get();
|
||||
@ -122,9 +122,21 @@ class PaymentController extends BaseController
|
||||
public function edit(PaymentRequest $request)
|
||||
{
|
||||
$payment = $request->entity();
|
||||
|
||||
$payment->payment_date = Utils::fromSqlDate($payment->payment_date);
|
||||
|
||||
$actions = [];
|
||||
if ($payment->invoiceJsonBackup()) {
|
||||
$actions[] = ['url' => url("/invoices/invoice_history/{$payment->invoice->public_id}?payment_id={$payment->public_id}"), 'label' => trans('texts.view_invoice')];
|
||||
}
|
||||
$actions[] = ['url' => url("/invoices/{$payment->invoice->public_id}/edit"), 'label' => trans('texts.edit_invoice')];
|
||||
$actions[] = DropdownButton::DIVIDER;
|
||||
if ( ! $payment->trashed()) {
|
||||
$actions[] = ['url' => 'javascript:submitAction("archive")', 'label' => trans('texts.archive_payment')];
|
||||
$actions[] = ['url' => 'javascript:onDeleteClick()', 'label' => trans('texts.delete_payment')];
|
||||
} else {
|
||||
$actions[] = ['url' => 'javascript:submitAction("restore")', 'label' => trans('texts.restore_expense')];
|
||||
}
|
||||
|
||||
$data = [
|
||||
'client' => null,
|
||||
'invoice' => null,
|
||||
@ -138,6 +150,7 @@ class PaymentController extends BaseController
|
||||
'method' => 'PUT',
|
||||
'url' => 'payments/'.$payment->public_id,
|
||||
'title' => trans('texts.edit_payment'),
|
||||
'actions' => $actions,
|
||||
'paymentTypes' => Cache::get('paymentTypes'),
|
||||
'clients' => Client::scope()->with('contacts')->orderBy('name')->get(),
|
||||
];
|
||||
@ -151,8 +164,10 @@ class PaymentController extends BaseController
|
||||
*/
|
||||
public function store(CreatePaymentRequest $request)
|
||||
{
|
||||
$input = $request->input();
|
||||
// check payment has been marked sent
|
||||
$request->invoice->markSentIfUnsent();
|
||||
|
||||
$input = $request->input();
|
||||
$input['invoice_id'] = Invoice::getPrivateId($input['invoice']);
|
||||
$input['client_id'] = Client::getPrivateId($input['client']);
|
||||
$payment = $this->paymentRepo->save($input);
|
||||
@ -173,6 +188,10 @@ class PaymentController extends BaseController
|
||||
*/
|
||||
public function update(UpdatePaymentRequest $request)
|
||||
{
|
||||
if (in_array($request->action, ['archive', 'delete', 'restore'])) {
|
||||
return self::bulk();
|
||||
}
|
||||
|
||||
$payment = $this->paymentRepo->save($request->input(), $request->entity());
|
||||
|
||||
Session::flash('message', trans('texts.updated_payment'));
|
||||
@ -191,7 +210,7 @@ class PaymentController extends BaseController
|
||||
$count = $this->paymentService->bulk($ids, $action, ['amount'=>$amount]);
|
||||
|
||||
if ($count > 0) {
|
||||
$message = Utils::pluralize($action=='refund'?'refunded_payment':$action.'d_payment', $count);
|
||||
$message = Utils::pluralize($action=='refund' ? 'refunded_payment':$action.'d_payment', $count);
|
||||
Session::flash('message', $message);
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,20 @@ class ProductApiController extends BaseAPIController
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Http\Response
|
||||
* @SWG\Get(
|
||||
* path="/products",
|
||||
* summary="List of products",
|
||||
* tags={"product"},
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="A list with products",
|
||||
* @SWG\Schema(type="array", @SWG\Items(ref="#/definitions/Product"))
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response="default",
|
||||
* description="an ""unexpected"" error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
@ -45,8 +58,25 @@ class ProductApiController extends BaseAPIController
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CreateProductRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
* @SWG\Post(
|
||||
* path="/products",
|
||||
* tags={"product"},
|
||||
* summary="Create a product",
|
||||
* @SWG\Parameter(
|
||||
* in="body",
|
||||
* name="body",
|
||||
* @SWG\Schema(ref="#/definitions/Product")
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="New product",
|
||||
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Product"))
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response="default",
|
||||
* description="an ""unexpected"" error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function store(CreateProductRequest $request)
|
||||
{
|
||||
@ -56,16 +86,32 @@ class ProductApiController extends BaseAPIController
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UpdateProductRequest $request
|
||||
* @param $publicId
|
||||
* @return \Illuminate\Http\Response
|
||||
* @SWG\Put(
|
||||
* path="/products/{product_id}",
|
||||
* tags={"product"},
|
||||
* summary="Update a product",
|
||||
* @SWG\Parameter(
|
||||
* in="body",
|
||||
* name="body",
|
||||
* @SWG\Schema(ref="#/definitions/Product")
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="Update product",
|
||||
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/Product"))
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response="default",
|
||||
* description="an ""unexpected"" error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function update(UpdateProductRequest $request, $publicId)
|
||||
{
|
||||
if ($request->action) {
|
||||
return $this->handleAction($request);
|
||||
}
|
||||
|
||||
|
||||
$data = $request->input();
|
||||
$data['public_id'] = $publicId;
|
||||
$product = $this->productRepo->save($data, $request->entity());
|
||||
|
@ -74,7 +74,7 @@ class ProductController extends BaseController
|
||||
|
||||
$data = [
|
||||
'account' => $account,
|
||||
'taxRates' => $account->invoice_item_taxes ? TaxRate::scope()->get(['id', 'name', 'rate']) : null,
|
||||
'taxRates' => $account->invoice_item_taxes ? TaxRate::scope()->whereIsInclusive(false)->get(['id', 'name', 'rate']) : null,
|
||||
'product' => $product,
|
||||
'entity' => $product,
|
||||
'method' => 'PUT',
|
||||
@ -94,7 +94,7 @@ class ProductController extends BaseController
|
||||
|
||||
$data = [
|
||||
'account' => $account,
|
||||
'taxRates' => $account->invoice_item_taxes ? TaxRate::scope()->get(['id', 'name', 'rate']) : null,
|
||||
'taxRates' => $account->invoice_item_taxes ? TaxRate::scope()->whereIsInclusive(false)->get(['id', 'name', 'rate']) : null,
|
||||
'product' => null,
|
||||
'method' => 'POST',
|
||||
'url' => 'products',
|
||||
|
@ -6,12 +6,9 @@ use Input;
|
||||
use Utils;
|
||||
use DB;
|
||||
use Session;
|
||||
use Str;
|
||||
use View;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Task;
|
||||
|
||||
/**
|
||||
* Class ReportController
|
||||
@ -67,19 +64,22 @@ class ReportController extends BaseController
|
||||
}
|
||||
|
||||
$reportTypes = [
|
||||
ENTITY_CLIENT => trans('texts.client'),
|
||||
ENTITY_INVOICE => trans('texts.invoice'),
|
||||
ENTITY_PRODUCT => trans('texts.product'),
|
||||
ENTITY_PAYMENT => trans('texts.payment'),
|
||||
ENTITY_EXPENSE => trans('texts.expense'),
|
||||
ENTITY_TASK => trans('texts.task'),
|
||||
ENTITY_TAX_RATE => trans('texts.tax'),
|
||||
'client',
|
||||
'product',
|
||||
'invoice',
|
||||
'invoice_details',
|
||||
'aging',
|
||||
'profit_and_loss',
|
||||
'payment',
|
||||
'expense',
|
||||
'task',
|
||||
'tax_rate',
|
||||
];
|
||||
|
||||
$params = [
|
||||
'startDate' => $startDate->format('Y-m-d'),
|
||||
'endDate' => $endDate->format('Y-m-d'),
|
||||
'reportTypes' => $reportTypes,
|
||||
'reportTypes' => array_combine($reportTypes, Utils::trans($reportTypes)),
|
||||
'reportType' => $reportType,
|
||||
'title' => trans('texts.charts_and_reports'),
|
||||
'account' => Auth::user()->account,
|
||||
@ -87,8 +87,18 @@ class ReportController extends BaseController
|
||||
|
||||
if (Auth::user()->account->hasFeature(FEATURE_REPORTS)) {
|
||||
$isExport = $action == 'export';
|
||||
$params = array_merge($params, self::generateReport($reportType, $startDate, $endDate, $dateField, $isExport));
|
||||
|
||||
$reportClass = '\\App\\Ninja\\Reports\\' . Str::studly($reportType) . 'Report';
|
||||
$options = [
|
||||
'date_field' => $dateField,
|
||||
'invoice_status' => request()->invoice_status,
|
||||
'group_dates_by' => request()->group_dates_by,
|
||||
];
|
||||
$report = new $reportClass($startDate, $endDate, $isExport, $options);
|
||||
if (Input::get('report_type')) {
|
||||
$report->run();
|
||||
}
|
||||
$params['report'] = $report;
|
||||
$params = array_merge($params, $report->results());
|
||||
if ($isExport) {
|
||||
self::export($reportType, $params['displayData'], $params['columns'], $params['reportTotals']);
|
||||
}
|
||||
@ -96,427 +106,12 @@ class ReportController extends BaseController
|
||||
$params['columns'] = [];
|
||||
$params['displayData'] = [];
|
||||
$params['reportTotals'] = [];
|
||||
$params['report'] = false;
|
||||
}
|
||||
|
||||
return View::make('reports.chart_builder', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $reportType
|
||||
* @param $startDate
|
||||
* @param $endDate
|
||||
* @param $dateField
|
||||
* @param $isExport
|
||||
* @return array
|
||||
*/
|
||||
private function generateReport($reportType, $startDate, $endDate, $dateField, $isExport)
|
||||
{
|
||||
if ($reportType == ENTITY_CLIENT) {
|
||||
return $this->generateClientReport($startDate, $endDate, $isExport);
|
||||
} elseif ($reportType == ENTITY_INVOICE) {
|
||||
return $this->generateInvoiceReport($startDate, $endDate, $isExport);
|
||||
} elseif ($reportType == ENTITY_PRODUCT) {
|
||||
return $this->generateProductReport($startDate, $endDate, $isExport);
|
||||
} elseif ($reportType == ENTITY_PAYMENT) {
|
||||
return $this->generatePaymentReport($startDate, $endDate, $isExport);
|
||||
} elseif ($reportType == ENTITY_TAX_RATE) {
|
||||
return $this->generateTaxRateReport($startDate, $endDate, $dateField, $isExport);
|
||||
} elseif ($reportType == ENTITY_EXPENSE) {
|
||||
return $this->generateExpenseReport($startDate, $endDate, $isExport);
|
||||
} elseif ($reportType == ENTITY_TASK) {
|
||||
return $this->generateTaskReport($startDate, $endDate, $isExport);
|
||||
}
|
||||
}
|
||||
|
||||
private function generateTaskReport($startDate, $endDate, $isExport)
|
||||
{
|
||||
$columns = ['client', 'date', 'description', 'duration'];
|
||||
$displayData = [];
|
||||
|
||||
$tasks = Task::scope()
|
||||
->with('client.contacts')
|
||||
->withArchived()
|
||||
->dateRange($startDate, $endDate);
|
||||
|
||||
foreach ($tasks->get() as $task) {
|
||||
$displayData[] = [
|
||||
$task->client ? ($isExport ? $task->client->getDisplayName() : $task->client->present()->link) : trans('texts.unassigned'),
|
||||
link_to($task->present()->url, $task->getStartTime()),
|
||||
$task->present()->description,
|
||||
Utils::formatTime($task->getDuration()),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $columns,
|
||||
'displayData' => $displayData,
|
||||
'reportTotals' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $startDate
|
||||
* @param $endDate
|
||||
* @param $dateField
|
||||
* @param $isExport
|
||||
* @return array
|
||||
*/
|
||||
private function generateTaxRateReport($startDate, $endDate, $dateField, $isExport)
|
||||
{
|
||||
$columns = ['tax_name', 'tax_rate', 'amount', 'paid'];
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$displayData = [];
|
||||
$reportTotals = [];
|
||||
|
||||
$clients = Client::scope()
|
||||
->withArchived()
|
||||
->with('contacts')
|
||||
->with(['invoices' => function($query) use ($startDate, $endDate, $dateField) {
|
||||
$query->with('invoice_items')->withArchived();
|
||||
if ($dateField == FILTER_INVOICE_DATE) {
|
||||
$query->where('invoice_date', '>=', $startDate)
|
||||
->where('invoice_date', '<=', $endDate)
|
||||
->with('payments');
|
||||
} else {
|
||||
$query->whereHas('payments', function($query) use ($startDate, $endDate) {
|
||||
$query->where('payment_date', '>=', $startDate)
|
||||
->where('payment_date', '<=', $endDate)
|
||||
->withArchived();
|
||||
})
|
||||
->with(['payments' => function($query) use ($startDate, $endDate) {
|
||||
$query->where('payment_date', '>=', $startDate)
|
||||
->where('payment_date', '<=', $endDate)
|
||||
->withArchived();
|
||||
}]);
|
||||
}
|
||||
}]);
|
||||
|
||||
foreach ($clients->get() as $client) {
|
||||
$currencyId = $client->currency_id ?: Auth::user()->account->getCurrencyId();
|
||||
$amount = 0;
|
||||
$paid = 0;
|
||||
$taxTotals = [];
|
||||
|
||||
foreach ($client->invoices as $invoice) {
|
||||
foreach ($invoice->getTaxes(true) as $key => $tax) {
|
||||
if ( ! isset($taxTotals[$currencyId])) {
|
||||
$taxTotals[$currencyId] = [];
|
||||
}
|
||||
if (isset($taxTotals[$currencyId][$key])) {
|
||||
$taxTotals[$currencyId][$key]['amount'] += $tax['amount'];
|
||||
$taxTotals[$currencyId][$key]['paid'] += $tax['paid'];
|
||||
} else {
|
||||
$taxTotals[$currencyId][$key] = $tax;
|
||||
}
|
||||
}
|
||||
|
||||
$amount += $invoice->amount;
|
||||
$paid += $invoice->getAmountPaid();
|
||||
}
|
||||
|
||||
foreach ($taxTotals as $currencyId => $taxes) {
|
||||
foreach ($taxes as $tax) {
|
||||
$displayData[] = [
|
||||
$tax['name'],
|
||||
$tax['rate'] . '%',
|
||||
$account->formatMoney($tax['amount'], $client),
|
||||
$account->formatMoney($tax['paid'], $client)
|
||||
];
|
||||
}
|
||||
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $tax['amount']);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $tax['paid']);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $columns,
|
||||
'displayData' => $displayData,
|
||||
'reportTotals' => $reportTotals,
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $startDate
|
||||
* @param $endDate
|
||||
* @param $isExport
|
||||
* @return array
|
||||
*/
|
||||
private function generatePaymentReport($startDate, $endDate, $isExport)
|
||||
{
|
||||
$columns = ['client', 'invoice_number', 'invoice_date', 'amount', 'payment_date', 'paid', 'method'];
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$displayData = [];
|
||||
$reportTotals = [];
|
||||
|
||||
$payments = Payment::scope()
|
||||
->withArchived()
|
||||
->excludeFailed()
|
||||
->whereHas('client', function($query) {
|
||||
$query->where('is_deleted', '=', false);
|
||||
})
|
||||
->whereHas('invoice', function($query) {
|
||||
$query->where('is_deleted', '=', false);
|
||||
})
|
||||
->with('client.contacts', 'invoice', 'payment_type', 'account_gateway.gateway')
|
||||
->where('payment_date', '>=', $startDate)
|
||||
->where('payment_date', '<=', $endDate);
|
||||
|
||||
foreach ($payments->get() as $payment) {
|
||||
$invoice = $payment->invoice;
|
||||
$client = $payment->client;
|
||||
$displayData[] = [
|
||||
$isExport ? $client->getDisplayName() : $client->present()->link,
|
||||
$isExport ? $invoice->invoice_number : $invoice->present()->link,
|
||||
$invoice->present()->invoice_date,
|
||||
$account->formatMoney($invoice->amount, $client),
|
||||
$payment->present()->payment_date,
|
||||
$account->formatMoney($payment->getCompletedAmount(), $client),
|
||||
$payment->present()->method,
|
||||
];
|
||||
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $invoice->amount);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $payment->getCompletedAmount());
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $columns,
|
||||
'displayData' => $displayData,
|
||||
'reportTotals' => $reportTotals,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $startDate
|
||||
* @param $endDate
|
||||
* @param $isExport
|
||||
* @return array
|
||||
*/
|
||||
private function generateInvoiceReport($startDate, $endDate, $isExport)
|
||||
{
|
||||
$columns = ['client', 'invoice_number', 'invoice_date', 'amount', 'payment_date', 'paid', 'method'];
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$displayData = [];
|
||||
$reportTotals = [];
|
||||
|
||||
$clients = Client::scope()
|
||||
->withTrashed()
|
||||
->with('contacts')
|
||||
->where('is_deleted', '=', false)
|
||||
->with(['invoices' => function($query) use ($startDate, $endDate) {
|
||||
$query->invoices()
|
||||
->withArchived()
|
||||
->where('invoice_date', '>=', $startDate)
|
||||
->where('invoice_date', '<=', $endDate)
|
||||
->with(['payments' => function($query) {
|
||||
$query->withArchived()
|
||||
->excludeFailed()
|
||||
->with('payment_type', 'account_gateway.gateway');
|
||||
}, 'invoice_items'])
|
||||
->withTrashed();
|
||||
}]);
|
||||
|
||||
foreach ($clients->get() as $client) {
|
||||
foreach ($client->invoices as $invoice) {
|
||||
|
||||
$payments = count($invoice->payments) ? $invoice->payments : [false];
|
||||
foreach ($payments as $payment) {
|
||||
$displayData[] = [
|
||||
$isExport ? $client->getDisplayName() : $client->present()->link,
|
||||
$isExport ? $invoice->invoice_number : $invoice->present()->link,
|
||||
$invoice->present()->invoice_date,
|
||||
$account->formatMoney($invoice->amount, $client),
|
||||
$payment ? $payment->present()->payment_date : '',
|
||||
$payment ? $account->formatMoney($payment->getCompletedAmount(), $client) : '',
|
||||
$payment ? $payment->present()->method : '',
|
||||
];
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $payment ? $payment->getCompletedAmount() : 0);
|
||||
}
|
||||
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $invoice->amount);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'balance', $invoice->balance);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $columns,
|
||||
'displayData' => $displayData,
|
||||
'reportTotals' => $reportTotals,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $startDate
|
||||
* @param $endDate
|
||||
* @param $isExport
|
||||
* @return array
|
||||
*/
|
||||
private function generateProductReport($startDate, $endDate, $isExport)
|
||||
{
|
||||
$columns = ['client', 'invoice_number', 'invoice_date', 'quantity', 'product'];
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$displayData = [];
|
||||
$reportTotals = [];
|
||||
|
||||
$clients = Client::scope()
|
||||
->withTrashed()
|
||||
->with('contacts')
|
||||
->where('is_deleted', '=', false)
|
||||
->with(['invoices' => function($query) use ($startDate, $endDate) {
|
||||
$query->where('invoice_date', '>=', $startDate)
|
||||
->where('invoice_date', '<=', $endDate)
|
||||
->where('is_deleted', '=', false)
|
||||
->where('is_recurring', '=', false)
|
||||
->where('invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
||||
->with(['invoice_items'])
|
||||
->withTrashed();
|
||||
}]);
|
||||
|
||||
foreach ($clients->get() as $client) {
|
||||
foreach ($client->invoices as $invoice) {
|
||||
|
||||
foreach ($invoice->invoice_items as $invoiceItem) {
|
||||
$displayData[] = [
|
||||
$isExport ? $client->getDisplayName() : $client->present()->link,
|
||||
$isExport ? $invoice->invoice_number : $invoice->present()->link,
|
||||
$invoice->present()->invoice_date,
|
||||
round($invoiceItem->qty, 2),
|
||||
$invoiceItem->product_key,
|
||||
];
|
||||
//$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $payment ? $payment->amount : 0);
|
||||
}
|
||||
|
||||
//$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $invoice->amount);
|
||||
//$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'balance', $invoice->balance);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $columns,
|
||||
'displayData' => $displayData,
|
||||
'reportTotals' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $startDate
|
||||
* @param $endDate
|
||||
* @param $isExport
|
||||
* @return array
|
||||
*/
|
||||
private function generateClientReport($startDate, $endDate, $isExport)
|
||||
{
|
||||
$columns = ['client', 'amount', 'paid', 'balance'];
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$displayData = [];
|
||||
$reportTotals = [];
|
||||
|
||||
$clients = Client::scope()
|
||||
->withArchived()
|
||||
->with('contacts')
|
||||
->with(['invoices' => function($query) use ($startDate, $endDate) {
|
||||
$query->where('invoice_date', '>=', $startDate)
|
||||
->where('invoice_date', '<=', $endDate)
|
||||
->where('invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
||||
->where('is_recurring', '=', false)
|
||||
->withArchived();
|
||||
}]);
|
||||
|
||||
foreach ($clients->get() as $client) {
|
||||
$amount = 0;
|
||||
$paid = 0;
|
||||
|
||||
foreach ($client->invoices as $invoice) {
|
||||
$amount += $invoice->amount;
|
||||
$paid += $invoice->getAmountPaid();
|
||||
}
|
||||
|
||||
$displayData[] = [
|
||||
$isExport ? $client->getDisplayName() : $client->present()->link,
|
||||
$account->formatMoney($amount, $client),
|
||||
$account->formatMoney($paid, $client),
|
||||
$account->formatMoney($amount - $paid, $client)
|
||||
];
|
||||
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'amount', $amount);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'paid', $paid);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $client->currency_id, 'balance', $amount - $paid);
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $columns,
|
||||
'displayData' => $displayData,
|
||||
'reportTotals' => $reportTotals,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $startDate
|
||||
* @param $endDate
|
||||
* @param $isExport
|
||||
* @return array
|
||||
*/
|
||||
private function generateExpenseReport($startDate, $endDate, $isExport)
|
||||
{
|
||||
$columns = ['vendor', 'client', 'date', 'expense_amount'];
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$displayData = [];
|
||||
$reportTotals = [];
|
||||
|
||||
$expenses = Expense::scope()
|
||||
->withArchived()
|
||||
->with('client.contacts', 'vendor')
|
||||
->where('expense_date', '>=', $startDate)
|
||||
->where('expense_date', '<=', $endDate);
|
||||
|
||||
|
||||
foreach ($expenses->get() as $expense) {
|
||||
$amount = $expense->amountWithTax();
|
||||
|
||||
$displayData[] = [
|
||||
$expense->vendor ? ($isExport ? $expense->vendor->name : $expense->vendor->present()->link) : '',
|
||||
$expense->client ? ($isExport ? $expense->client->getDisplayName() : $expense->client->present()->link) : '',
|
||||
$expense->present()->expense_date,
|
||||
Utils::formatMoney($amount, $expense->currency_id),
|
||||
];
|
||||
|
||||
$reportTotals = $this->addToTotals($reportTotals, $expense->expense_currency_id, 'amount', $amount);
|
||||
$reportTotals = $this->addToTotals($reportTotals, $expense->invoice_currency_id, 'amount', 0);
|
||||
}
|
||||
|
||||
return [
|
||||
'columns' => $columns,
|
||||
'displayData' => $displayData,
|
||||
'reportTotals' => $reportTotals,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $currencyId
|
||||
* @param $field
|
||||
* @param $value
|
||||
* @return mixed
|
||||
*/
|
||||
private function addToTotals($data, $currencyId, $field, $value) {
|
||||
$currencyId = $currencyId ?: Auth::user()->account->getCurrencyId();
|
||||
|
||||
if (!isset($data[$currencyId][$field])) {
|
||||
$data[$currencyId][$field] = 0;
|
||||
}
|
||||
|
||||
$data[$currencyId][$field] += $value;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $reportType
|
||||
* @param $data
|
||||
@ -534,6 +129,7 @@ class ReportController extends BaseController
|
||||
|
||||
Utils::exportData($output, $data, Utils::trans($columns));
|
||||
|
||||
/*
|
||||
fwrite($output, trans('texts.totals'));
|
||||
foreach ($totals as $currencyId => $fields) {
|
||||
foreach ($fields as $key => $value) {
|
||||
@ -550,6 +146,7 @@ class ReportController extends BaseController
|
||||
}
|
||||
fwrite($output, $csv."\n");
|
||||
}
|
||||
*/
|
||||
|
||||
fclose($output);
|
||||
exit;
|
||||
|
@ -253,10 +253,11 @@ class TaskController extends BaseController
|
||||
Session::flash('message', trans('texts.stopped_task'));
|
||||
return Redirect::to('tasks');
|
||||
} else if ($action == 'invoice' || $action == 'add_to_invoice') {
|
||||
$tasks = Task::scope($ids)->with('client')->get();
|
||||
$tasks = Task::scope($ids)->with('client')->orderBy('project_id', 'id')->get();
|
||||
$clientPublicId = false;
|
||||
$data = [];
|
||||
|
||||
$lastProjectId = false;
|
||||
foreach ($tasks as $task) {
|
||||
if ($task->client) {
|
||||
if (!$clientPublicId) {
|
||||
@ -276,11 +277,13 @@ class TaskController extends BaseController
|
||||
}
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$showProject = $lastProjectId != $task->project_id;
|
||||
$data[] = [
|
||||
'publicId' => $task->public_id,
|
||||
'description' => $task->description . "\n\n" . $task->present()->times($account),
|
||||
'description' => $task->present()->invoiceDescription($account, $showProject),
|
||||
'duration' => $task->getHours(),
|
||||
];
|
||||
$lastProjectId = $task->project_id;
|
||||
}
|
||||
|
||||
if ($action == 'invoice') {
|
||||
|
@ -28,6 +28,22 @@ class TaxRateApiController extends BaseAPIController
|
||||
$this->taxRateRepo = $taxRateRepo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @SWG\Get(
|
||||
* path="/tax_rates",
|
||||
* summary="List of tax rates",
|
||||
* tags={"tax_rate"},
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="A list with tax rates",
|
||||
* @SWG\Schema(type="array", @SWG\Items(ref="#/definitions/TaxRate"))
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response="default",
|
||||
* description="an ""unexpected"" error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$taxRates = TaxRate::scope()
|
||||
@ -37,6 +53,27 @@ class TaxRateApiController extends BaseAPIController
|
||||
return $this->listResponse($taxRates);
|
||||
}
|
||||
|
||||
/**
|
||||
* @SWG\Post(
|
||||
* path="/tax_rates",
|
||||
* tags={"tax_rate"},
|
||||
* summary="Create a tax rate",
|
||||
* @SWG\Parameter(
|
||||
* in="body",
|
||||
* name="body",
|
||||
* @SWG\Schema(ref="#/definitions/TaxRate")
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="New tax rate",
|
||||
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/TaxRate"))
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response="default",
|
||||
* description="an ""unexpected"" error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function store(CreateTaxRateRequest $request)
|
||||
{
|
||||
$taxRate = $this->taxRateRepo->save($request->input());
|
||||
@ -45,16 +82,32 @@ class TaxRateApiController extends BaseAPIController
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UpdateTaxRateRequest $request
|
||||
* @param $publicId
|
||||
* @return \Illuminate\Http\Response
|
||||
* @SWG\Put(
|
||||
* path="/tax_rates/{tax_rate_id}",
|
||||
* tags={"tax_rate"},
|
||||
* summary="Update a tax rate",
|
||||
* @SWG\Parameter(
|
||||
* in="body",
|
||||
* name="body",
|
||||
* @SWG\Schema(ref="#/definitions/TaxRate")
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="Update tax rate",
|
||||
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/TaxRate"))
|
||||
* ),
|
||||
* @SWG\Response(
|
||||
* response="default",
|
||||
* description="an ""unexpected"" error"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function update(UpdateTaxRateRequest $request, $publicId)
|
||||
{
|
||||
if ($request->action) {
|
||||
return $this->handleAction($request);
|
||||
}
|
||||
|
||||
|
||||
$data = $request->input();
|
||||
$data['public_id'] = $publicId;
|
||||
$taxRate = $this->taxRateRepo->save($data, $request->entity());
|
||||
|
@ -240,7 +240,7 @@ class UserController extends BaseController
|
||||
$user = User::where('confirmation_code', '=', $code)->get()->first();
|
||||
|
||||
if ($user) {
|
||||
$notice_msg = trans('texts.security.confirmation');
|
||||
$notice_msg = trans('texts.security_confirmation');
|
||||
|
||||
$user->confirmed = true;
|
||||
$user->confirmation_code = '';
|
||||
@ -356,7 +356,7 @@ class UserController extends BaseController
|
||||
Session::put(SESSION_USER_ACCOUNTS, $users);
|
||||
|
||||
Session::flash('message', trans('texts.unlinked_account'));
|
||||
return Redirect::to('/dashboard');
|
||||
return Redirect::to('/manage_companies');
|
||||
}
|
||||
|
||||
public function manageCompanies()
|
||||
|
@ -82,7 +82,6 @@ class VendorController extends BaseController
|
||||
'actionLinks' => $actionLinks,
|
||||
'showBreadcrumbs' => false,
|
||||
'vendor' => $vendor,
|
||||
'totalexpense' => $vendor->getTotalExpense(),
|
||||
'title' => trans('texts.view_vendor'),
|
||||
'hasRecurringInvoices' => false,
|
||||
'hasQuotes' => false,
|
||||
|
@ -25,7 +25,9 @@ class ApiCheck {
|
||||
{
|
||||
$loggingIn = $request->is('api/v1/login')
|
||||
|| $request->is('api/v1/register')
|
||||
|| $request->is('api/v1/oauth_login');
|
||||
|| $request->is('api/v1/oauth_login')
|
||||
|| $request->is('api/v1/ping');
|
||||
|
||||
$headers = Utils::getApiHeaders();
|
||||
$hasApiSecret = false;
|
||||
|
||||
@ -38,7 +40,8 @@ class ApiCheck {
|
||||
// check API secret
|
||||
if ( ! $hasApiSecret) {
|
||||
sleep(ERROR_DELAY);
|
||||
return Response::json('Invalid value for API_SECRET', 403, $headers);
|
||||
$error['error'] = ['message'=>'Invalid value for API_SECRET'];
|
||||
return Response::json($error, 403, $headers);
|
||||
}
|
||||
} else {
|
||||
// check for a valid token
|
||||
@ -50,7 +53,8 @@ class ApiCheck {
|
||||
Session::set('token_id', $token->id);
|
||||
} else {
|
||||
sleep(ERROR_DELAY);
|
||||
return Response::json('Invalid token', 403, $headers);
|
||||
$error['error'] = ['message'=>'Invalid token'];
|
||||
return Response::json($error, 403, $headers);
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,7 +63,8 @@ class ApiCheck {
|
||||
}
|
||||
|
||||
if (!Utils::hasFeature(FEATURE_API) && !$hasApiSecret) {
|
||||
return Response::json('API requires pro plan', 403, $headers);
|
||||
$error['error'] = ['message'=>'API requires pro plan'];
|
||||
return Response::json($error, 403, $headers);
|
||||
} else {
|
||||
$key = Auth::check() ? Auth::user()->account->id : $request->getClientIp();
|
||||
|
||||
|
@ -149,7 +149,8 @@ class StartupCheck
|
||||
$company = Auth::user()->account->company;
|
||||
$company->plan_term = PLAN_TERM_YEARLY;
|
||||
$company->plan_paid = $data;
|
||||
$company->plan_expires = date_create($data)->modify('+1 year')->format('Y-m-d');
|
||||
$date = max(date_create($data), date_create($company->plan_expires));
|
||||
$company->plan_expires = $date->modify('+1 year')->format('Y-m-d');
|
||||
$company->plan = PLAN_WHITE_LABEL;
|
||||
$company->save();
|
||||
|
||||
|
@ -19,8 +19,14 @@ class CreateClientRequest extends ClientRequest
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
$rules = [
|
||||
'contacts' => 'valid_contacts',
|
||||
];
|
||||
|
||||
if ($this->user()->account->client_number_counter) {
|
||||
$rules['id_number'] = 'unique:clients,id_number,,id,account_id,' . $this->user()->account_id;
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Client;
|
||||
|
||||
class CreateInvoiceAPIRequest extends InvoiceRequest
|
||||
{
|
||||
/**
|
||||
@ -31,6 +33,11 @@ class CreateInvoiceAPIRequest extends InvoiceRequest
|
||||
//'end_date' => 'date',
|
||||
];
|
||||
|
||||
if ($this->user()->account->client_number_counter) {
|
||||
$clientId = Client::getPrivateId(request()->input('client')['public_id']);
|
||||
$rules['client.id_number'] = 'unique:clients,id_number,'.$clientId.',id,account_id,' . $this->user()->account_id;
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Client;
|
||||
|
||||
class CreateInvoiceRequest extends InvoiceRequest
|
||||
{
|
||||
/**
|
||||
@ -30,6 +32,11 @@ class CreateInvoiceRequest extends InvoiceRequest
|
||||
//'end_date' => 'date',
|
||||
];
|
||||
|
||||
if ($this->user()->account->client_number_counter) {
|
||||
$clientId = Client::getPrivateId(request()->input('client')['public_id']);
|
||||
$rules['client.id_number'] = 'unique:clients,id_number,'.$clientId.',id,account_id,' . $this->user()->account_id;
|
||||
}
|
||||
|
||||
/* There's a problem parsing the dates
|
||||
if (Request::get('is_recurring') && Request::get('start_date') && Request::get('end_date')) {
|
||||
$rules['end_date'] = 'after' . Request::get('start_date');
|
||||
|
@ -33,9 +33,8 @@ class CreatePaymentAPIRequest extends PaymentRequest
|
||||
];
|
||||
}
|
||||
|
||||
$invoice = Invoice::scope($this->invoice_id)
|
||||
$this->invoice = $invoice = Invoice::scope($this->invoice_id)
|
||||
->invoices()
|
||||
->whereIsPublic(true)
|
||||
->firstOrFail();
|
||||
|
||||
$this->merge([
|
||||
|
@ -22,9 +22,8 @@ class CreatePaymentRequest extends PaymentRequest
|
||||
public function rules()
|
||||
{
|
||||
$input = $this->input();
|
||||
$invoice = Invoice::scope($input['invoice'])
|
||||
$this->invoice = $invoice = Invoice::scope($input['invoice'])
|
||||
->invoices()
|
||||
->whereIsPublic(true)
|
||||
->firstOrFail();
|
||||
|
||||
$rules = [
|
||||
|
58
app/Http/Requests/SaveClientPortalSettings.php
Normal file
58
app/Http/Requests/SaveClientPortalSettings.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php namespace App\Http\Requests;
|
||||
|
||||
use Utils;
|
||||
use HTMLUtils;
|
||||
|
||||
class SaveClientPortalSettings extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return $this->user()->is_admin && $this->user()->isPro();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
if ($this->custom_link == 'subdomain' && Utils::isNinja()) {
|
||||
$rules['subdomain'] = "unique:accounts,subdomain,{$this->user()->account_id},id|valid_subdomain";
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function sanitize()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
if ($this->client_view_css && Utils::isNinja()) {
|
||||
$input['client_view_css'] = HTMLUtils::sanitize($this->client_view_css);
|
||||
}
|
||||
|
||||
if ($this->custom_link == 'subdomain') {
|
||||
$subdomain = substr(strtolower($input['subdomain']), 0, MAX_SUBDOMAIN_LENGTH);
|
||||
$input['subdomain'] = preg_replace('/[^a-zA-Z0-9_\-\.]/', '', $subdomain);
|
||||
$input['iframe_url'] = null;
|
||||
} else {
|
||||
$iframeURL = substr(strtolower($input['iframe_url']), 0, MAX_IFRAME_URL_LENGTH);
|
||||
$iframeURL = preg_replace('/[^a-zA-Z0-9_\-\:\/\.]/', '', $iframeURL);
|
||||
$input['iframe_url'] = rtrim($iframeURL, '/');
|
||||
$input['subdomain'] = null;
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
|
||||
return $this->all();
|
||||
}
|
||||
|
||||
}
|
27
app/Http/Requests/SaveEmailSettings.php
Normal file
27
app/Http/Requests/SaveEmailSettings.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php namespace App\Http\Requests;
|
||||
|
||||
class SaveEmailSettings extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return $this->user()->is_admin && $this->user()->isPro();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'bcc_email' => 'email',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -19,8 +19,14 @@ class UpdateClientRequest extends ClientRequest
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
$rules = [
|
||||
'contacts' => 'valid_contacts',
|
||||
];
|
||||
|
||||
if ($this->user()->account->client_number_counter) {
|
||||
$rules['id_number'] = 'unique:clients,id_number,'.$this->entity()->id.',id,account_id,' . $this->user()->account_id;
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Client;
|
||||
|
||||
class UpdateInvoiceAPIRequest extends InvoiceRequest
|
||||
{
|
||||
/**
|
||||
@ -35,6 +37,11 @@ class UpdateInvoiceAPIRequest extends InvoiceRequest
|
||||
//'end_date' => 'date',
|
||||
];
|
||||
|
||||
if ($this->user()->account->client_number_counter) {
|
||||
$clientId = Client::getPrivateId(request()->input('client')['public_id']);
|
||||
$rules['client.id_number'] = 'unique:clients,id_number,'.$clientId.',id,account_id,' . $this->user()->account_id;
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Client;
|
||||
|
||||
class UpdateInvoiceRequest extends InvoiceRequest
|
||||
{
|
||||
/**
|
||||
@ -32,6 +34,11 @@ class UpdateInvoiceRequest extends InvoiceRequest
|
||||
//'end_date' => 'date',
|
||||
];
|
||||
|
||||
if ($this->user()->account->client_number_counter) {
|
||||
$clientId = Client::getPrivateId(request()->input('client')['public_id']);
|
||||
$rules['client.id_number'] = 'unique:clients,id_number,'.$clientId.',id,account_id,' . $this->user()->account_id;
|
||||
}
|
||||
|
||||
/* There's a problem parsing the dates
|
||||
if (Request::get('is_recurring') && Request::get('start_date') && Request::get('end_date')) {
|
||||
$rules['end_date'] = 'after' . Request::get('start_date');
|
||||
|
@ -11,14 +11,6 @@
|
||||
|
|
||||
*/
|
||||
|
||||
//Crypt::decrypt();
|
||||
//apc_clear_cache();
|
||||
//dd(DB::getQueryLog());
|
||||
//dd(Client::getPrivateId(1));
|
||||
//dd(new DateTime());
|
||||
//dd(App::environment());
|
||||
//dd(gethostname());
|
||||
//Log::error('test');
|
||||
|
||||
// Application setup
|
||||
Route::get('/setup', 'AppController@showSetup');
|
||||
@ -28,7 +20,6 @@ Route::get('/update', 'AppController@update');
|
||||
|
||||
// Public pages
|
||||
Route::get('/', 'HomeController@showIndex');
|
||||
Route::get('/terms', 'HomeController@showTerms');
|
||||
Route::get('/log_error', 'HomeController@logError');
|
||||
Route::get('/invoice_now', 'HomeController@invoiceNow');
|
||||
Route::get('/keep_alive', 'HomeController@keepAlive');
|
||||
@ -131,6 +122,7 @@ Route::group(['middleware' => 'auth:user'], function() {
|
||||
Route::get('account/get_search_data', ['as' => 'get_search_data', 'uses' => 'AccountController@getSearchData']);
|
||||
Route::get('check_invoice_number/{invoice_id?}', 'InvoiceController@checkInvoiceNumber');
|
||||
Route::post('save_sidebar_state', 'UserController@saveSidebarState');
|
||||
Route::post('contact_us', 'HomeController@contactUs');
|
||||
|
||||
Route::get('settings/user_details', 'AccountController@showUserDetails');
|
||||
Route::post('settings/user_details', 'AccountController@saveUserDetails');
|
||||
@ -141,10 +133,11 @@ Route::group(['middleware' => 'auth:user'], function() {
|
||||
Route::get('api/clients', 'ClientController@getDatatable');
|
||||
Route::get('api/activities/{client_id?}', 'ActivityController@getDatatable');
|
||||
Route::post('clients/bulk', 'ClientController@bulk');
|
||||
Route::get('clients/statement/{client_id}', 'ClientController@statement');
|
||||
|
||||
Route::resource('tasks', 'TaskController');
|
||||
Route::get('api/tasks/{client_id?}', 'TaskController@getDatatable');
|
||||
Route::get('tasks/create/{client_id?}', 'TaskController@create');
|
||||
Route::get('tasks/create/{client_id?}/{project_id?}', 'TaskController@create');
|
||||
Route::post('tasks/bulk', 'TaskController@bulk');
|
||||
Route::get('projects', 'ProjectController@index');
|
||||
Route::get('api/projects', 'ProjectController@getDatatable');
|
||||
@ -211,7 +204,7 @@ Route::group(['middleware' => 'auth:user'], function() {
|
||||
|
||||
// Expense
|
||||
Route::resource('expenses', 'ExpenseController');
|
||||
Route::get('expenses/create/{vendor_id?}/{client_id?}', 'ExpenseController@create');
|
||||
Route::get('expenses/create/{vendor_id?}/{client_id?}/{category_id?}', 'ExpenseController@create');
|
||||
Route::get('api/expenses', 'ExpenseController@getDatatable');
|
||||
Route::get('api/expenses/{id}', 'ExpenseController@getDatatableVendor');
|
||||
Route::post('expenses/bulk', 'ExpenseController@bulk');
|
||||
@ -227,6 +220,7 @@ Route::group(['middleware' => 'auth:user'], function() {
|
||||
Route::post('bluevine/signup', 'BlueVineController@signup');
|
||||
Route::get('bluevine/hide_message', 'BlueVineController@hideMessage');
|
||||
Route::get('bluevine/completed', 'BlueVineController@handleCompleted');
|
||||
Route::get('white_label/hide_message', 'NinjaController@hideWhiteLabelMessage');
|
||||
});
|
||||
|
||||
Route::group([
|
||||
@ -237,8 +231,6 @@ Route::group([
|
||||
Route::resource('users', 'UserController');
|
||||
Route::post('users/bulk', 'UserController@bulk');
|
||||
Route::get('send_confirmation/{user_id}', 'UserController@sendConfirmation');
|
||||
Route::get('start_trial/{plan}', 'AccountController@startTrial')
|
||||
->where(['plan'=>'pro']);
|
||||
Route::get('/switch_account/{user_id}', 'UserController@switchAccount');
|
||||
Route::get('/unlink_account/{user_account_id}/{user_id}', 'UserController@unlinkAccount');
|
||||
Route::get('/manage_companies', 'UserController@manageCompanies');
|
||||
@ -252,10 +244,12 @@ Route::group([
|
||||
Route::post('tax_rates/bulk', 'TaxRateController@bulk');
|
||||
|
||||
Route::get('settings/email_preview', 'AccountController@previewEmail');
|
||||
Route::post('settings/client_portal', 'AccountController@saveClientPortalSettings');
|
||||
Route::post('settings/email_settings', 'AccountController@saveEmailSettings');
|
||||
Route::get('company/{section}/{subSection?}', 'AccountController@redirectLegacy');
|
||||
Route::get('settings/data_visualizations', 'ReportController@d3');
|
||||
Route::get('settings/reports', 'ReportController@showReports');
|
||||
Route::post('settings/reports', 'ReportController@showReports');
|
||||
Route::get('reports', 'ReportController@showReports');
|
||||
Route::post('reports', 'ReportController@showReports');
|
||||
|
||||
Route::post('settings/change_plan', 'AccountController@changePlan');
|
||||
Route::post('settings/cancel_account', 'AccountController@cancelAccount');
|
||||
@ -359,573 +353,6 @@ Route::get('/comments/feed', function() {
|
||||
return Redirect::to(NINJA_WEB_URL.'/comments/feed', 301);
|
||||
});
|
||||
|
||||
if (!defined('CONTACT_EMAIL')) {
|
||||
define('CONTACT_EMAIL', config('mail.from.address'));
|
||||
define('CONTACT_NAME', config('mail.from.name'));
|
||||
define('SITE_URL', config('app.url'));
|
||||
|
||||
define('ENV_DEVELOPMENT', 'local');
|
||||
define('ENV_STAGING', 'staging');
|
||||
|
||||
define('RECENTLY_VIEWED', 'recent_history');
|
||||
|
||||
define('ENTITY_CLIENT', 'client');
|
||||
define('ENTITY_CONTACT', 'contact');
|
||||
define('ENTITY_INVOICE', 'invoice');
|
||||
define('ENTITY_DOCUMENT', 'document');
|
||||
define('ENTITY_INVOICE_ITEM', 'invoice_item');
|
||||
define('ENTITY_INVITATION', 'invitation');
|
||||
define('ENTITY_RECURRING_INVOICE', 'recurring_invoice');
|
||||
define('ENTITY_PAYMENT', 'payment');
|
||||
define('ENTITY_CREDIT', 'credit');
|
||||
define('ENTITY_QUOTE', 'quote');
|
||||
define('ENTITY_TASK', 'task');
|
||||
define('ENTITY_ACCOUNT_GATEWAY', 'account_gateway');
|
||||
define('ENTITY_USER', 'user');
|
||||
define('ENTITY_TOKEN', 'token');
|
||||
define('ENTITY_TAX_RATE', 'tax_rate');
|
||||
define('ENTITY_PRODUCT', 'product');
|
||||
define('ENTITY_ACTIVITY', 'activity');
|
||||
define('ENTITY_VENDOR', 'vendor');
|
||||
define('ENTITY_VENDOR_ACTIVITY', 'vendor_activity');
|
||||
define('ENTITY_EXPENSE', 'expense');
|
||||
define('ENTITY_PAYMENT_TERM', 'payment_term');
|
||||
define('ENTITY_EXPENSE_ACTIVITY', 'expense_activity');
|
||||
define('ENTITY_BANK_ACCOUNT', 'bank_account');
|
||||
define('ENTITY_BANK_SUBACCOUNT', 'bank_subaccount');
|
||||
define('ENTITY_EXPENSE_CATEGORY', 'expense_category');
|
||||
define('ENTITY_PROJECT', 'project');
|
||||
|
||||
define('INVOICE_TYPE_STANDARD', 1);
|
||||
define('INVOICE_TYPE_QUOTE', 2);
|
||||
|
||||
define('PERSON_CONTACT', 'contact');
|
||||
define('PERSON_USER', 'user');
|
||||
define('PERSON_VENDOR_CONTACT','vendorcontact');
|
||||
|
||||
define('BASIC_SETTINGS', 'basic_settings');
|
||||
define('ADVANCED_SETTINGS', 'advanced_settings');
|
||||
|
||||
define('ACCOUNT_COMPANY_DETAILS', 'company_details');
|
||||
define('ACCOUNT_USER_DETAILS', 'user_details');
|
||||
define('ACCOUNT_LOCALIZATION', 'localization');
|
||||
define('ACCOUNT_NOTIFICATIONS', 'notifications');
|
||||
define('ACCOUNT_IMPORT_EXPORT', 'import_export');
|
||||
define('ACCOUNT_MANAGEMENT', 'account_management');
|
||||
define('ACCOUNT_PAYMENTS', 'online_payments');
|
||||
define('ACCOUNT_BANKS', 'bank_accounts');
|
||||
define('ACCOUNT_IMPORT_EXPENSES', 'import_expenses');
|
||||
define('ACCOUNT_MAP', 'import_map');
|
||||
define('ACCOUNT_EXPORT', 'export');
|
||||
define('ACCOUNT_TAX_RATES', 'tax_rates');
|
||||
define('ACCOUNT_PRODUCTS', 'products');
|
||||
define('ACCOUNT_ADVANCED_SETTINGS', 'advanced_settings');
|
||||
define('ACCOUNT_INVOICE_SETTINGS', 'invoice_settings');
|
||||
define('ACCOUNT_INVOICE_DESIGN', 'invoice_design');
|
||||
define('ACCOUNT_CLIENT_PORTAL', 'client_portal');
|
||||
define('ACCOUNT_EMAIL_SETTINGS', 'email_settings');
|
||||
define('ACCOUNT_REPORTS', 'reports');
|
||||
define('ACCOUNT_USER_MANAGEMENT', 'user_management');
|
||||
define('ACCOUNT_DATA_VISUALIZATIONS', 'data_visualizations');
|
||||
define('ACCOUNT_TEMPLATES_AND_REMINDERS', 'templates_and_reminders');
|
||||
define('ACCOUNT_API_TOKENS', 'api_tokens');
|
||||
define('ACCOUNT_CUSTOMIZE_DESIGN', 'customize_design');
|
||||
define('ACCOUNT_SYSTEM_SETTINGS', 'system_settings');
|
||||
define('ACCOUNT_PAYMENT_TERMS','payment_terms');
|
||||
|
||||
define('ACTION_RESTORE', 'restore');
|
||||
define('ACTION_ARCHIVE', 'archive');
|
||||
define('ACTION_CLONE', 'clone');
|
||||
define('ACTION_CONVERT', 'convert');
|
||||
define('ACTION_DELETE', 'delete');
|
||||
|
||||
define('ACTIVITY_TYPE_CREATE_CLIENT', 1);
|
||||
define('ACTIVITY_TYPE_ARCHIVE_CLIENT', 2);
|
||||
define('ACTIVITY_TYPE_DELETE_CLIENT', 3);
|
||||
define('ACTIVITY_TYPE_CREATE_INVOICE', 4);
|
||||
define('ACTIVITY_TYPE_UPDATE_INVOICE', 5);
|
||||
define('ACTIVITY_TYPE_EMAIL_INVOICE', 6);
|
||||
define('ACTIVITY_TYPE_VIEW_INVOICE', 7);
|
||||
define('ACTIVITY_TYPE_ARCHIVE_INVOICE', 8);
|
||||
define('ACTIVITY_TYPE_DELETE_INVOICE', 9);
|
||||
define('ACTIVITY_TYPE_CREATE_PAYMENT', 10);
|
||||
//define('ACTIVITY_TYPE_UPDATE_PAYMENT', 11);
|
||||
define('ACTIVITY_TYPE_ARCHIVE_PAYMENT', 12);
|
||||
define('ACTIVITY_TYPE_DELETE_PAYMENT', 13);
|
||||
define('ACTIVITY_TYPE_CREATE_CREDIT', 14);
|
||||
//define('ACTIVITY_TYPE_UPDATE_CREDIT', 15);
|
||||
define('ACTIVITY_TYPE_ARCHIVE_CREDIT', 16);
|
||||
define('ACTIVITY_TYPE_DELETE_CREDIT', 17);
|
||||
define('ACTIVITY_TYPE_CREATE_QUOTE', 18);
|
||||
define('ACTIVITY_TYPE_UPDATE_QUOTE', 19);
|
||||
define('ACTIVITY_TYPE_EMAIL_QUOTE', 20);
|
||||
define('ACTIVITY_TYPE_VIEW_QUOTE', 21);
|
||||
define('ACTIVITY_TYPE_ARCHIVE_QUOTE', 22);
|
||||
define('ACTIVITY_TYPE_DELETE_QUOTE', 23);
|
||||
define('ACTIVITY_TYPE_RESTORE_QUOTE', 24);
|
||||
define('ACTIVITY_TYPE_RESTORE_INVOICE', 25);
|
||||
define('ACTIVITY_TYPE_RESTORE_CLIENT', 26);
|
||||
define('ACTIVITY_TYPE_RESTORE_PAYMENT', 27);
|
||||
define('ACTIVITY_TYPE_RESTORE_CREDIT', 28);
|
||||
define('ACTIVITY_TYPE_APPROVE_QUOTE', 29);
|
||||
define('ACTIVITY_TYPE_CREATE_VENDOR', 30);
|
||||
define('ACTIVITY_TYPE_ARCHIVE_VENDOR', 31);
|
||||
define('ACTIVITY_TYPE_DELETE_VENDOR', 32);
|
||||
define('ACTIVITY_TYPE_RESTORE_VENDOR', 33);
|
||||
define('ACTIVITY_TYPE_CREATE_EXPENSE', 34);
|
||||
define('ACTIVITY_TYPE_ARCHIVE_EXPENSE', 35);
|
||||
define('ACTIVITY_TYPE_DELETE_EXPENSE', 36);
|
||||
define('ACTIVITY_TYPE_RESTORE_EXPENSE', 37);
|
||||
define('ACTIVITY_TYPE_VOIDED_PAYMENT', 39);
|
||||
define('ACTIVITY_TYPE_REFUNDED_PAYMENT', 40);
|
||||
define('ACTIVITY_TYPE_FAILED_PAYMENT', 41);
|
||||
define('ACTIVITY_TYPE_CREATE_TASK', 42);
|
||||
define('ACTIVITY_TYPE_UPDATE_TASK', 43);
|
||||
define('ACTIVITY_TYPE_ARCHIVE_TASK', 44);
|
||||
define('ACTIVITY_TYPE_DELETE_TASK', 45);
|
||||
define('ACTIVITY_TYPE_RESTORE_TASK', 46);
|
||||
define('ACTIVITY_TYPE_UPDATE_EXPENSE', 47);
|
||||
|
||||
|
||||
define('DEFAULT_INVOICE_NUMBER', '0001');
|
||||
define('RECENTLY_VIEWED_LIMIT', 20);
|
||||
define('LOGGED_ERROR_LIMIT', 100);
|
||||
define('RANDOM_KEY_LENGTH', 32);
|
||||
define('MAX_NUM_USERS', 20);
|
||||
define('MAX_IMPORT_ROWS', 500);
|
||||
define('MAX_SUBDOMAIN_LENGTH', 30);
|
||||
define('MAX_IFRAME_URL_LENGTH', 250);
|
||||
define('MAX_LOGO_FILE_SIZE', 200); // KB
|
||||
define('MAX_FAILED_LOGINS', 10);
|
||||
define('MAX_INVOICE_ITEMS', env('MAX_INVOICE_ITEMS', 100));
|
||||
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)
|
||||
define('DOCUMENT_PREVIEW_SIZE', env('DOCUMENT_PREVIEW_SIZE', 300));// pixels
|
||||
define('DEFAULT_FONT_SIZE', 9);
|
||||
define('DEFAULT_HEADER_FONT', 1);// Roboto
|
||||
define('DEFAULT_BODY_FONT', 1);// Roboto
|
||||
define('DEFAULT_SEND_RECURRING_HOUR', 8);
|
||||
|
||||
define('IMPORT_CSV', 'CSV');
|
||||
define('IMPORT_JSON', 'JSON');
|
||||
define('IMPORT_FRESHBOOKS', 'FreshBooks');
|
||||
define('IMPORT_WAVE', 'Wave');
|
||||
define('IMPORT_RONIN', 'Ronin');
|
||||
define('IMPORT_HIVEAGE', 'Hiveage');
|
||||
define('IMPORT_ZOHO', 'Zoho');
|
||||
define('IMPORT_NUTCACHE', 'Nutcache');
|
||||
define('IMPORT_INVOICEABLE', 'Invoiceable');
|
||||
define('IMPORT_HARVEST', 'Harvest');
|
||||
|
||||
define('MAX_NUM_CLIENTS', 100);
|
||||
define('MAX_NUM_CLIENTS_PRO', 20000);
|
||||
define('MAX_NUM_CLIENTS_LEGACY', 500);
|
||||
define('MAX_INVOICE_AMOUNT', 1000000000);
|
||||
define('LEGACY_CUTOFF', 57800);
|
||||
define('ERROR_DELAY', 3);
|
||||
|
||||
define('MAX_NUM_VENDORS', 100);
|
||||
define('MAX_NUM_VENDORS_PRO', 20000);
|
||||
|
||||
define('STATUS_ACTIVE', 'active');
|
||||
define('STATUS_ARCHIVED', 'archived');
|
||||
define('STATUS_DELETED', 'deleted');
|
||||
|
||||
define('INVOICE_STATUS_DRAFT', 1);
|
||||
define('INVOICE_STATUS_SENT', 2);
|
||||
define('INVOICE_STATUS_VIEWED', 3);
|
||||
define('INVOICE_STATUS_APPROVED', 4);
|
||||
define('INVOICE_STATUS_PARTIAL', 5);
|
||||
define('INVOICE_STATUS_PAID', 6);
|
||||
define('INVOICE_STATUS_OVERDUE', 7);
|
||||
|
||||
define('PAYMENT_STATUS_PENDING', 1);
|
||||
define('PAYMENT_STATUS_VOIDED', 2);
|
||||
define('PAYMENT_STATUS_FAILED', 3);
|
||||
define('PAYMENT_STATUS_COMPLETED', 4);
|
||||
define('PAYMENT_STATUS_PARTIALLY_REFUNDED', 5);
|
||||
define('PAYMENT_STATUS_REFUNDED', 6);
|
||||
|
||||
define('TASK_STATUS_LOGGED', 1);
|
||||
define('TASK_STATUS_RUNNING', 2);
|
||||
define('TASK_STATUS_INVOICED', 3);
|
||||
define('TASK_STATUS_PAID', 4);
|
||||
|
||||
define('EXPENSE_STATUS_LOGGED', 1);
|
||||
define('EXPENSE_STATUS_INVOICED', 2);
|
||||
define('EXPENSE_STATUS_PAID', 3);
|
||||
|
||||
define('CUSTOM_DESIGN', 11);
|
||||
|
||||
define('FREQUENCY_WEEKLY', 1);
|
||||
define('FREQUENCY_TWO_WEEKS', 2);
|
||||
define('FREQUENCY_FOUR_WEEKS', 3);
|
||||
define('FREQUENCY_MONTHLY', 4);
|
||||
define('FREQUENCY_THREE_MONTHS', 5);
|
||||
define('FREQUENCY_SIX_MONTHS', 6);
|
||||
define('FREQUENCY_ANNUALLY', 7);
|
||||
|
||||
define('SESSION_TIMEZONE', 'timezone');
|
||||
define('SESSION_CURRENCY', 'currency');
|
||||
define('SESSION_CURRENCY_DECORATOR', 'currency_decorator');
|
||||
define('SESSION_DATE_FORMAT', 'dateFormat');
|
||||
define('SESSION_DATE_PICKER_FORMAT', 'datePickerFormat');
|
||||
define('SESSION_DATETIME_FORMAT', 'datetimeFormat');
|
||||
define('SESSION_COUNTER', 'sessionCounter');
|
||||
define('SESSION_LOCALE', 'sessionLocale');
|
||||
define('SESSION_USER_ACCOUNTS', 'userAccounts');
|
||||
define('SESSION_REFERRAL_CODE', 'referralCode');
|
||||
define('SESSION_LEFT_SIDEBAR', 'showLeftSidebar');
|
||||
define('SESSION_RIGHT_SIDEBAR', 'showRightSidebar');
|
||||
|
||||
define('SESSION_LAST_REQUEST_PAGE', 'SESSION_LAST_REQUEST_PAGE');
|
||||
define('SESSION_LAST_REQUEST_TIME', 'SESSION_LAST_REQUEST_TIME');
|
||||
|
||||
define('CURRENCY_DOLLAR', 1);
|
||||
define('CURRENCY_EURO', 3);
|
||||
|
||||
define('DEFAULT_TIMEZONE', 'US/Eastern');
|
||||
define('DEFAULT_COUNTRY', 840); // United Stated
|
||||
define('DEFAULT_CURRENCY', CURRENCY_DOLLAR);
|
||||
define('DEFAULT_LANGUAGE', 1); // English
|
||||
define('DEFAULT_DATE_FORMAT', 'M j, Y');
|
||||
define('DEFAULT_DATE_PICKER_FORMAT', 'M d, yyyy');
|
||||
define('DEFAULT_DATETIME_FORMAT', 'F j, Y g:i a');
|
||||
define('DEFAULT_DATETIME_MOMENT_FORMAT', 'MMM D, YYYY h:mm:ss a');
|
||||
define('DEFAULT_LOCALE', 'en');
|
||||
define('DEFAULT_MAP_ZOOM', 10);
|
||||
|
||||
define('RESULT_SUCCESS', 'success');
|
||||
define('RESULT_FAILURE', 'failure');
|
||||
|
||||
|
||||
define('PAYMENT_LIBRARY_OMNIPAY', 1);
|
||||
define('PAYMENT_LIBRARY_PHP_PAYMENTS', 2);
|
||||
|
||||
define('GATEWAY_AUTHORIZE_NET', 1);
|
||||
define('GATEWAY_EWAY', 4);
|
||||
define('GATEWAY_MOLLIE', 9);
|
||||
define('GATEWAY_PAYFAST', 13);
|
||||
define('GATEWAY_PAYPAL_EXPRESS', 17);
|
||||
define('GATEWAY_PAYPAL_PRO', 18);
|
||||
define('GATEWAY_SAGE_PAY_DIRECT', 20);
|
||||
define('GATEWAY_SAGE_PAY_SERVER', 21);
|
||||
define('GATEWAY_STRIPE', 23);
|
||||
define('GATEWAY_GOCARDLESS', 6);
|
||||
define('GATEWAY_TWO_CHECKOUT', 27);
|
||||
define('GATEWAY_BEANSTREAM', 29);
|
||||
define('GATEWAY_PSIGATE', 30);
|
||||
define('GATEWAY_MOOLAH', 31);
|
||||
define('GATEWAY_BITPAY', 42);
|
||||
define('GATEWAY_DWOLLA', 43);
|
||||
define('GATEWAY_CHECKOUT_COM', 47);
|
||||
define('GATEWAY_CYBERSOURCE', 49);
|
||||
define('GATEWAY_WEPAY', 60);
|
||||
define('GATEWAY_BRAINTREE', 61);
|
||||
define('GATEWAY_CUSTOM', 62);
|
||||
|
||||
// The customer exists, but only as a local concept
|
||||
// The remote gateway doesn't understand the concept of customers
|
||||
define('CUSTOMER_REFERENCE_LOCAL', 'local');
|
||||
|
||||
define('EVENT_CREATE_CLIENT', 1);
|
||||
define('EVENT_CREATE_INVOICE', 2);
|
||||
define('EVENT_CREATE_QUOTE', 3);
|
||||
define('EVENT_CREATE_PAYMENT', 4);
|
||||
define('EVENT_CREATE_VENDOR',5);
|
||||
|
||||
define('REQUESTED_PRO_PLAN', 'REQUESTED_PRO_PLAN');
|
||||
define('DEMO_ACCOUNT_ID', 'DEMO_ACCOUNT_ID');
|
||||
define('PREV_USER_ID', 'PREV_USER_ID');
|
||||
define('NINJA_ACCOUNT_KEY', 'zg4ylmzDkdkPOT8yoKQw9LTWaoZJx79h');
|
||||
define('NINJA_GATEWAY_ID', GATEWAY_STRIPE);
|
||||
define('NINJA_GATEWAY_CONFIG', 'NINJA_GATEWAY_CONFIG');
|
||||
define('NINJA_WEB_URL', env('NINJA_WEB_URL', 'https://www.invoiceninja.com'));
|
||||
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', '2.9.5' . env('NINJA_VERSION_SUFFIX'));
|
||||
|
||||
define('SOCIAL_LINK_FACEBOOK', env('SOCIAL_LINK_FACEBOOK', 'https://www.facebook.com/invoiceninja'));
|
||||
define('SOCIAL_LINK_TWITTER', env('SOCIAL_LINK_TWITTER', 'https://twitter.com/invoiceninja'));
|
||||
define('SOCIAL_LINK_GITHUB', env('SOCIAL_LINK_GITHUB', 'https://github.com/invoiceninja/invoiceninja/'));
|
||||
|
||||
define('NINJA_FORUM_URL', env('NINJA_FORUM_URL', 'https://www.invoiceninja.com/forums/forum/support/'));
|
||||
define('NINJA_CONTACT_URL', env('NINJA_CONTACT_URL', 'https://www.invoiceninja.com/contact/'));
|
||||
define('NINJA_FROM_EMAIL', env('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com'));
|
||||
define('RELEASES_URL', env('RELEASES_URL', 'https://trello.com/b/63BbiVVe/invoice-ninja'));
|
||||
define('ZAPIER_URL', env('ZAPIER_URL', 'https://zapier.com/zapbook/invoice-ninja'));
|
||||
define('OUTDATE_BROWSER_URL', env('OUTDATE_BROWSER_URL', 'http://browsehappy.com/'));
|
||||
define('PDFMAKE_DOCS', env('PDFMAKE_DOCS', 'http://pdfmake.org/playground.html'));
|
||||
define('PHANTOMJS_CLOUD', env('PHANTOMJS_CLOUD', 'http://api.phantomjscloud.com/api/browser/v2/'));
|
||||
define('PHP_DATE_FORMATS', env('PHP_DATE_FORMATS', 'http://php.net/manual/en/function.date.php'));
|
||||
define('REFERRAL_PROGRAM_URL', env('REFERRAL_PROGRAM_URL', 'https://www.invoiceninja.com/referral-program/'));
|
||||
define('EMAIL_MARKUP_URL', env('EMAIL_MARKUP_URL', 'https://developers.google.com/gmail/markup'));
|
||||
define('OFX_HOME_URL', env('OFX_HOME_URL', 'http://www.ofxhome.com/index.php/home/directory/all'));
|
||||
define('GOOGLE_ANALYITCS_URL', env('GOOGLE_ANALYITCS_URL', 'https://www.google-analytics.com/collect'));
|
||||
define('TRANSIFEX_URL', env('TRANSIFEX_URL', 'https://www.transifex.com/invoice-ninja/invoice-ninja'));
|
||||
define('CHROME_PDF_HELP_URL', 'https://support.google.com/chrome/answer/6213030?hl=en');
|
||||
define('FIREFOX_PDF_HELP_URL', 'https://support.mozilla.org/en-US/kb/view-pdf-files-firefox');
|
||||
|
||||
define('MSBOT_LOGIN_URL', 'https://login.microsoftonline.com/common/oauth2/v2.0/token');
|
||||
define('MSBOT_LUIS_URL', 'https://api.projectoxford.ai/luis/v1/application');
|
||||
define('SKYPE_API_URL', 'https://apis.skype.com/v3');
|
||||
define('MSBOT_STATE_URL', 'https://state.botframework.com/v3');
|
||||
|
||||
define('BLANK_IMAGE', 'data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=');
|
||||
|
||||
define('COUNT_FREE_DESIGNS', 4);
|
||||
define('COUNT_FREE_DESIGNS_SELF_HOST', 5); // include the custom design
|
||||
define('PRODUCT_ONE_CLICK_INSTALL', 1);
|
||||
define('PRODUCT_INVOICE_DESIGNS', 2);
|
||||
define('PRODUCT_WHITE_LABEL', 3);
|
||||
define('PRODUCT_SELF_HOST', 4);
|
||||
define('WHITE_LABEL_AFFILIATE_KEY', '92D2J5');
|
||||
define('INVOICE_DESIGNS_AFFILIATE_KEY', 'T3RS74');
|
||||
define('SELF_HOST_AFFILIATE_KEY', '8S69AD');
|
||||
|
||||
define('PLAN_PRICE_PRO_MONTHLY', env('PLAN_PRICE_PRO_MONTHLY', 8));
|
||||
define('PLAN_PRICE_ENTERPRISE_MONTHLY_2', env('PLAN_PRICE_ENTERPRISE_MONTHLY_2', 12));
|
||||
define('PLAN_PRICE_ENTERPRISE_MONTHLY_5', env('PLAN_PRICE_ENTERPRISE_MONTHLY_5', 18));
|
||||
define('PLAN_PRICE_ENTERPRISE_MONTHLY_10', env('PLAN_PRICE_ENTERPRISE_MONTHLY_10', 24));
|
||||
define('WHITE_LABEL_PRICE', env('WHITE_LABEL_PRICE', 20));
|
||||
define('INVOICE_DESIGNS_PRICE', env('INVOICE_DESIGNS_PRICE', 10));
|
||||
|
||||
define('USER_TYPE_SELF_HOST', 'SELF_HOST');
|
||||
define('USER_TYPE_CLOUD_HOST', 'CLOUD_HOST');
|
||||
define('NEW_VERSION_AVAILABLE', 'NEW_VERSION_AVAILABLE');
|
||||
|
||||
define('TEST_USERNAME', 'user@example.com');
|
||||
define('TEST_PASSWORD', 'password');
|
||||
define('API_SECRET', 'API_SECRET');
|
||||
define('DEFAULT_API_PAGE_SIZE', 15);
|
||||
define('MAX_API_PAGE_SIZE', 500);
|
||||
|
||||
define('IOS_PUSH_CERTIFICATE', env('IOS_PUSH_CERTIFICATE', ''));
|
||||
|
||||
define('TOKEN_BILLING_DISABLED', 1);
|
||||
define('TOKEN_BILLING_OPT_IN', 2);
|
||||
define('TOKEN_BILLING_OPT_OUT', 3);
|
||||
define('TOKEN_BILLING_ALWAYS', 4);
|
||||
|
||||
define('PAYMENT_TYPE_CREDIT', 1);
|
||||
define('PAYMENT_TYPE_ACH', 5);
|
||||
define('PAYMENT_TYPE_VISA', 6);
|
||||
define('PAYMENT_TYPE_MASTERCARD', 7);
|
||||
define('PAYMENT_TYPE_AMERICAN_EXPRESS', 8);
|
||||
define('PAYMENT_TYPE_DISCOVER', 9);
|
||||
define('PAYMENT_TYPE_DINERS', 10);
|
||||
define('PAYMENT_TYPE_EUROCARD', 11);
|
||||
define('PAYMENT_TYPE_NOVA', 12);
|
||||
define('PAYMENT_TYPE_CREDIT_CARD_OTHER', 13);
|
||||
define('PAYMENT_TYPE_PAYPAL', 14);
|
||||
define('PAYMENT_TYPE_CARTE_BLANCHE', 17);
|
||||
define('PAYMENT_TYPE_UNIONPAY', 18);
|
||||
define('PAYMENT_TYPE_JCB', 19);
|
||||
define('PAYMENT_TYPE_LASER', 20);
|
||||
define('PAYMENT_TYPE_MAESTRO', 21);
|
||||
define('PAYMENT_TYPE_SOLO', 22);
|
||||
define('PAYMENT_TYPE_SWITCH', 23);
|
||||
|
||||
define('PAYMENT_METHOD_STATUS_NEW', 'new');
|
||||
define('PAYMENT_METHOD_STATUS_VERIFICATION_FAILED', 'verification_failed');
|
||||
define('PAYMENT_METHOD_STATUS_VERIFIED', 'verified');
|
||||
|
||||
define('GATEWAY_TYPE_CREDIT_CARD', 1);
|
||||
define('GATEWAY_TYPE_BANK_TRANSFER', 2);
|
||||
define('GATEWAY_TYPE_PAYPAL', 3);
|
||||
define('GATEWAY_TYPE_BITCOIN', 4);
|
||||
define('GATEWAY_TYPE_DWOLLA', 5);
|
||||
define('GATEWAY_TYPE_CUSTOM', 6);
|
||||
define('GATEWAY_TYPE_TOKEN', 'token');
|
||||
|
||||
define('REMINDER1', 'reminder1');
|
||||
define('REMINDER2', 'reminder2');
|
||||
define('REMINDER3', 'reminder3');
|
||||
|
||||
define('REMINDER_DIRECTION_AFTER', 1);
|
||||
define('REMINDER_DIRECTION_BEFORE', 2);
|
||||
|
||||
define('REMINDER_FIELD_DUE_DATE', 1);
|
||||
define('REMINDER_FIELD_INVOICE_DATE', 2);
|
||||
|
||||
define('FILTER_INVOICE_DATE', 'invoice_date');
|
||||
define('FILTER_PAYMENT_DATE', 'payment_date');
|
||||
|
||||
define('SOCIAL_GOOGLE', 'Google');
|
||||
define('SOCIAL_FACEBOOK', 'Facebook');
|
||||
define('SOCIAL_GITHUB', 'GitHub');
|
||||
define('SOCIAL_LINKEDIN', 'LinkedIn');
|
||||
|
||||
define('USER_STATE_ACTIVE', 'active');
|
||||
define('USER_STATE_PENDING', 'pending');
|
||||
define('USER_STATE_DISABLED', 'disabled');
|
||||
define('USER_STATE_ADMIN', 'admin');
|
||||
define('USER_STATE_OWNER', 'owner');
|
||||
|
||||
define('API_SERIALIZER_ARRAY', 'array');
|
||||
define('API_SERIALIZER_JSON', 'json');
|
||||
|
||||
define('EMAIL_DESIGN_PLAIN', 1);
|
||||
define('EMAIL_DESIGN_LIGHT', 2);
|
||||
define('EMAIL_DESIGN_DARK', 3);
|
||||
|
||||
define('BANK_LIBRARY_OFX', 1);
|
||||
|
||||
define('CURRENCY_DECORATOR_CODE', 'code');
|
||||
define('CURRENCY_DECORATOR_SYMBOL', 'symbol');
|
||||
define('CURRENCY_DECORATOR_NONE', 'none');
|
||||
|
||||
define('RESELLER_REVENUE_SHARE', 'A');
|
||||
define('RESELLER_LIMITED_USERS', 'B');
|
||||
|
||||
define('AUTO_BILL_OFF', 1);
|
||||
define('AUTO_BILL_OPT_IN', 2);
|
||||
define('AUTO_BILL_OPT_OUT', 3);
|
||||
define('AUTO_BILL_ALWAYS', 4);
|
||||
|
||||
// These must be lowercase
|
||||
define('PLAN_FREE', 'free');
|
||||
define('PLAN_PRO', 'pro');
|
||||
define('PLAN_ENTERPRISE', 'enterprise');
|
||||
define('PLAN_WHITE_LABEL', 'white_label');
|
||||
define('PLAN_TERM_MONTHLY', 'month');
|
||||
define('PLAN_TERM_YEARLY', 'year');
|
||||
|
||||
// Pro
|
||||
define('FEATURE_CUSTOMIZE_INVOICE_DESIGN', 'customize_invoice_design');
|
||||
define('FEATURE_REMOVE_CREATED_BY', 'remove_created_by');
|
||||
define('FEATURE_DIFFERENT_DESIGNS', 'different_designs');
|
||||
define('FEATURE_EMAIL_TEMPLATES_REMINDERS', 'email_templates_reminders');
|
||||
define('FEATURE_INVOICE_SETTINGS', 'invoice_settings');
|
||||
define('FEATURE_CUSTOM_EMAILS', 'custom_emails');
|
||||
define('FEATURE_PDF_ATTACHMENT', 'pdf_attachment');
|
||||
define('FEATURE_MORE_INVOICE_DESIGNS', 'more_invoice_designs');
|
||||
define('FEATURE_QUOTES', 'quotes');
|
||||
define('FEATURE_TASKS', 'tasks');
|
||||
define('FEATURE_EXPENSES', 'expenses');
|
||||
define('FEATURE_REPORTS', 'reports');
|
||||
define('FEATURE_BUY_NOW_BUTTONS', 'buy_now_buttons');
|
||||
define('FEATURE_API', 'api');
|
||||
define('FEATURE_CLIENT_PORTAL_PASSWORD', 'client_portal_password');
|
||||
define('FEATURE_CUSTOM_URL', 'custom_url');
|
||||
|
||||
define('FEATURE_MORE_CLIENTS', 'more_clients'); // No trial allowed
|
||||
|
||||
// Whitelabel
|
||||
define('FEATURE_CLIENT_PORTAL_CSS', 'client_portal_css');
|
||||
define('FEATURE_WHITE_LABEL', 'feature_white_label');
|
||||
|
||||
// Enterprise
|
||||
define('FEATURE_DOCUMENTS', 'documents');
|
||||
|
||||
// No Trial allowed
|
||||
define('FEATURE_USERS', 'users');// Grandfathered for old Pro users
|
||||
define('FEATURE_USER_PERMISSIONS', 'user_permissions');
|
||||
|
||||
// Pro users who started paying on or before this date will be able to manage users
|
||||
define('PRO_USERS_GRANDFATHER_DEADLINE', '2016-06-04');
|
||||
define('EXTRAS_GRANDFATHER_COMPANY_ID', 35089);
|
||||
|
||||
// WePay
|
||||
define('WEPAY_PRODUCTION', 'production');
|
||||
define('WEPAY_STAGE', 'stage');
|
||||
define('WEPAY_CLIENT_ID', env('WEPAY_CLIENT_ID'));
|
||||
define('WEPAY_CLIENT_SECRET', env('WEPAY_CLIENT_SECRET'));
|
||||
define('WEPAY_AUTO_UPDATE', env('WEPAY_AUTO_UPDATE', false));
|
||||
define('WEPAY_ENVIRONMENT', env('WEPAY_ENVIRONMENT', WEPAY_PRODUCTION));
|
||||
define('WEPAY_ENABLE_CANADA', env('WEPAY_ENABLE_CANADA', false));
|
||||
define('WEPAY_THEME', env('WEPAY_THEME','{"name":"Invoice Ninja","primary_color":"0b4d78","secondary_color":"0b4d78","background_color":"f8f8f8","button_color":"33b753"}'));
|
||||
|
||||
define('SKYPE_CARD_RECEIPT', 'message/card.receipt');
|
||||
define('SKYPE_CARD_CAROUSEL', 'message/card.carousel');
|
||||
define('SKYPE_CARD_HERO', '');
|
||||
|
||||
define('BOT_STATE_GET_EMAIL', 'get_email');
|
||||
define('BOT_STATE_GET_CODE', 'get_code');
|
||||
define('BOT_STATE_READY', 'ready');
|
||||
define('SIMILAR_MIN_THRESHOLD', 50);
|
||||
|
||||
// https://docs.botframework.com/en-us/csharp/builder/sdkreference/attachments.html
|
||||
define('SKYPE_BUTTON_OPEN_URL', 'openUrl');
|
||||
define('SKYPE_BUTTON_IM_BACK', 'imBack');
|
||||
define('SKYPE_BUTTON_POST_BACK', 'postBack');
|
||||
define('SKYPE_BUTTON_CALL', 'call'); // "tel:123123123123"
|
||||
define('SKYPE_BUTTON_PLAY_AUDIO', 'playAudio');
|
||||
define('SKYPE_BUTTON_PLAY_VIDEO', 'playVideo');
|
||||
define('SKYPE_BUTTON_SHOW_IMAGE', 'showImage');
|
||||
define('SKYPE_BUTTON_DOWNLOAD_FILE', 'downloadFile');
|
||||
|
||||
define('INVOICE_FIELDS_CLIENT', 'client_fields');
|
||||
define('INVOICE_FIELDS_INVOICE', 'invoice_fields');
|
||||
define('INVOICE_FIELDS_ACCOUNT', 'account_fields');
|
||||
|
||||
$creditCards = [
|
||||
1 => ['card' => 'images/credit_cards/Test-Visa-Icon.png', 'text' => 'Visa'],
|
||||
2 => ['card' => 'images/credit_cards/Test-MasterCard-Icon.png', 'text' => 'Master Card'],
|
||||
4 => ['card' => 'images/credit_cards/Test-AmericanExpress-Icon.png', 'text' => 'American Express'],
|
||||
8 => ['card' => 'images/credit_cards/Test-Diners-Icon.png', 'text' => 'Diners'],
|
||||
16 => ['card' => 'images/credit_cards/Test-Discover-Icon.png', 'text' => 'Discover']
|
||||
];
|
||||
define('CREDIT_CARDS', serialize($creditCards));
|
||||
|
||||
$cachedTables = [
|
||||
'currencies' => 'App\Models\Currency',
|
||||
'sizes' => 'App\Models\Size',
|
||||
'industries' => 'App\Models\Industry',
|
||||
'timezones' => 'App\Models\Timezone',
|
||||
'dateFormats' => 'App\Models\DateFormat',
|
||||
'datetimeFormats' => 'App\Models\DatetimeFormat',
|
||||
'languages' => 'App\Models\Language',
|
||||
'paymentTerms' => 'App\Models\PaymentTerm',
|
||||
'paymentTypes' => 'App\Models\PaymentType',
|
||||
'countries' => 'App\Models\Country',
|
||||
'invoiceDesigns' => 'App\Models\InvoiceDesign',
|
||||
'invoiceStatus' => 'App\Models\InvoiceStatus',
|
||||
'frequencies' => 'App\Models\Frequency',
|
||||
'gateways' => 'App\Models\Gateway',
|
||||
'gatewayTypes' => 'App\Models\GatewayType',
|
||||
'fonts' => 'App\Models\Font',
|
||||
'banks' => 'App\Models\Bank',
|
||||
];
|
||||
define('CACHED_TABLES', serialize($cachedTables));
|
||||
|
||||
function uctrans($text)
|
||||
{
|
||||
return ucwords(trans($text));
|
||||
}
|
||||
|
||||
// optional trans: only return the string if it's translated
|
||||
function otrans($text)
|
||||
{
|
||||
$locale = Session::get(SESSION_LOCALE);
|
||||
|
||||
if ($locale == 'en') {
|
||||
return trans($text);
|
||||
} else {
|
||||
$string = trans($text);
|
||||
$english = trans($text, [], 'en');
|
||||
return $string != $english ? $string : '';
|
||||
}
|
||||
}
|
||||
|
||||
// include modules in translations
|
||||
function mtrans($entityType, $text = false)
|
||||
{
|
||||
if ( ! $text) {
|
||||
$text = $entityType;
|
||||
}
|
||||
|
||||
if ( ! Utils::isNinjaProd() && $module = Module::find($entityType)) {
|
||||
return trans("{$module->getLowerName()}::texts.{$text}");
|
||||
} else {
|
||||
return trans("texts.{$text}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
if (Utils::isNinjaDev())
|
||||
{
|
||||
@ -934,3 +361,6 @@ if (Utils::isNinjaDev())
|
||||
Auth::loginUsingId(1);
|
||||
}
|
||||
*/
|
||||
|
||||
// Include static app constants
|
||||
require_once app_path() . '/Constants.php';
|
||||
|
47
app/Jobs/Job.php
Normal file
47
app/Jobs/Job.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Ninja\Mailers\ContactMailer;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Monolog\Logger;
|
||||
|
||||
abstract class Job
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* The name of the job.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $jobName;
|
||||
|
||||
/**
|
||||
* Handle a job failure.
|
||||
*
|
||||
* @param ContactMailer $mailer
|
||||
* @param Logger $logger
|
||||
*/
|
||||
/*
|
||||
protected function failed(ContactMailer $mailer, Logger $logger)
|
||||
{
|
||||
if(config('queue.failed.notify_email')) {
|
||||
$mailer->sendTo(
|
||||
config('queue.failed.notify_email'),
|
||||
config('mail.from.address'),
|
||||
config('mail.from.name'),
|
||||
config('queue.failed.notify_subject', trans('texts.job_failed', ['name'=>$this->jobName])),
|
||||
'job_failed',
|
||||
[
|
||||
'name' => $this->jobName,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$logger->error(
|
||||
trans('texts.job_failed', ['name' => $this->jobName])
|
||||
);
|
||||
}
|
||||
*/
|
||||
}
|
73
app/Jobs/SendInvoiceEmail.php
Normal file
73
app/Jobs/SendInvoiceEmail.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use App\Ninja\Mailers\ContactMailer;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Monolog\Logger;
|
||||
use Carbon;
|
||||
|
||||
/**
|
||||
* Class SendInvoiceEmail
|
||||
*/
|
||||
class SendInvoiceEmail extends Job implements ShouldQueue
|
||||
{
|
||||
use InteractsWithQueue, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var Invoice
|
||||
*/
|
||||
protected $invoice;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $reminder;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $pdfString;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param Invoice $invoice
|
||||
* @param string $pdf
|
||||
* @param bool $reminder
|
||||
*/
|
||||
public function __construct(Invoice $invoice, $reminder = false, $pdfString = false)
|
||||
{
|
||||
$this->invoice = $invoice;
|
||||
$this->reminder = $reminder;
|
||||
$this->pdfString = $pdfString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @param ContactMailer $mailer
|
||||
*/
|
||||
public function handle(ContactMailer $mailer)
|
||||
{
|
||||
$mailer->sendInvoice($this->invoice, $this->reminder, $this->pdfString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a job failure.
|
||||
*
|
||||
* @param ContactMailer $mailer
|
||||
* @param Logger $logger
|
||||
*/
|
||||
/*
|
||||
public function failed(ContactMailer $mailer, Logger $logger)
|
||||
{
|
||||
$this->jobName = $this->job->getName();
|
||||
|
||||
parent::failed($mailer, $logger);
|
||||
}
|
||||
*/
|
||||
}
|
66
app/Jobs/SendNotificationEmail.php
Normal file
66
app/Jobs/SendNotificationEmail.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Payment;
|
||||
use App\Ninja\Mailers\UserMailer;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Monolog\Logger;
|
||||
use Carbon;
|
||||
|
||||
/**
|
||||
* Class SendInvoiceEmail
|
||||
*/
|
||||
class SendNotificationEmail extends Job implements ShouldQueue
|
||||
{
|
||||
use InteractsWithQueue, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* @var Invoice
|
||||
*/
|
||||
protected $invoice;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* @var Payment
|
||||
*/
|
||||
protected $payment;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
|
||||
* @param UserMailer $userMailer
|
||||
* @param ContactMailer $contactMailer
|
||||
* @param PushService $pushService
|
||||
*/
|
||||
public function __construct($user, $invoice, $type, $payment)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->invoice = $invoice;
|
||||
$this->type = $type;
|
||||
$this->payment = $payment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @param ContactMailer $mailer
|
||||
*/
|
||||
public function handle(UserMailer $userMailer)
|
||||
{
|
||||
$userMailer->sendNotification($this->user, $this->invoice, $this->type, $this->payment);
|
||||
}
|
||||
|
||||
}
|
47
app/Jobs/SendPaymentEmail.php
Normal file
47
app/Jobs/SendPaymentEmail.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Payment;
|
||||
use App\Ninja\Mailers\ContactMailer;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Monolog\Logger;
|
||||
use Carbon;
|
||||
|
||||
/**
|
||||
* Class SendInvoiceEmail
|
||||
*/
|
||||
class SendPaymentEmail extends Job implements ShouldQueue
|
||||
{
|
||||
use InteractsWithQueue, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var Payment
|
||||
*/
|
||||
protected $payment;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
|
||||
* @param Payment $payment
|
||||
*/
|
||||
public function __construct($payment)
|
||||
{
|
||||
$this->payment = $payment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @param ContactMailer $mailer
|
||||
*/
|
||||
public function handle(ContactMailer $contactMailer)
|
||||
{
|
||||
$contactMailer->sendPaymentConfirmation($this->payment);
|
||||
}
|
||||
|
||||
|
||||
}
|
52
app/Jobs/SendPushNotification.php
Normal file
52
app/Jobs/SendPushNotification.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use App\Services\PushService;
|
||||
use Monolog\Logger;
|
||||
use Carbon;
|
||||
|
||||
/**
|
||||
* Class SendInvoiceEmail
|
||||
*/
|
||||
class SendPushNotification extends Job implements ShouldQueue
|
||||
{
|
||||
use InteractsWithQueue, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var Invoice
|
||||
*/
|
||||
protected $invoice;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
|
||||
* @param Invoice $invoice
|
||||
*/
|
||||
public function __construct($invoice, $type)
|
||||
{
|
||||
$this->invoice = $invoice;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @param PushService $pushService
|
||||
*/
|
||||
public function handle(PushService $pushService)
|
||||
{
|
||||
$pushService->sendNotification($this->invoice, $this->type);
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
<?php namespace App\Libraries;
|
||||
|
||||
use JonnyW\PhantomJs\Client;
|
||||
|
||||
class CurlUtils
|
||||
{
|
||||
public static function post($url, $data, $headers = false)
|
||||
@ -38,4 +40,28 @@ class CurlUtils
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function phantom($method, $url)
|
||||
{
|
||||
if ( ! $path = env('PHANTOMJS_BIN_PATH')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$client = Client::getInstance();
|
||||
$client->getEngine()->setPath($path);
|
||||
|
||||
$request = $client->getMessageFactory()->createRequest($url, $method);
|
||||
$response = $client->getMessageFactory()->createResponse();
|
||||
|
||||
// Send the request
|
||||
$client->send($request, $response);
|
||||
|
||||
if ($response->getStatus() === 200) {
|
||||
return $response->getContent();
|
||||
} else {
|
||||
//$response->getStatus();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
37
app/Libraries/HTMLUtils.php
Normal file
37
app/Libraries/HTMLUtils.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php namespace App\Libraries;
|
||||
|
||||
use HTMLPurifier;
|
||||
use HTMLPurifier_Config;
|
||||
|
||||
class HTMLUtils
|
||||
{
|
||||
public static function sanitize($css)
|
||||
{
|
||||
// Allow referencing the body element
|
||||
$css = preg_replace('/(?<![a-z0-9\-\_\#\.])body(?![a-z0-9\-\_])/i', '.body', $css);
|
||||
|
||||
//
|
||||
// Inspired by http://stackoverflow.com/a/5209050/1721527, dleavitt <https://stackoverflow.com/users/362110/dleavitt>
|
||||
//
|
||||
|
||||
// Create a new configuration object
|
||||
$config = HTMLPurifier_Config::createDefault();
|
||||
$config->set('Filter.ExtractStyleBlocks', true);
|
||||
$config->set('CSS.AllowImportant', true);
|
||||
$config->set('CSS.AllowTricky', true);
|
||||
$config->set('CSS.Trusted', true);
|
||||
|
||||
// Create a new purifier instance
|
||||
$purifier = new HTMLPurifier($config);
|
||||
|
||||
// Wrap our CSS in style tags and pass to purifier.
|
||||
// we're not actually interested in the html response though
|
||||
$purifier->purify('<style>'.$css.'</style>');
|
||||
|
||||
// The "style" blocks are stored seperately
|
||||
$css = $purifier->context->get('StyleBlocks');
|
||||
|
||||
// Get the first style block
|
||||
return count($css) ? $css[0] : '';
|
||||
}
|
||||
}
|
@ -67,6 +67,11 @@ class Utils
|
||||
return self::isNinjaProd() || self::isNinjaDev();
|
||||
}
|
||||
|
||||
public static function isSelfHost()
|
||||
{
|
||||
return ! static::isNinjaProd();
|
||||
}
|
||||
|
||||
public static function isNinjaProd()
|
||||
{
|
||||
if (Utils::isReseller()) {
|
||||
@ -97,13 +102,21 @@ class Utils
|
||||
|
||||
public static function isWhiteLabel()
|
||||
{
|
||||
if (Utils::isNinjaProd()) {
|
||||
return false;
|
||||
$account = false;
|
||||
|
||||
if (Utils::isNinja()) {
|
||||
if (Auth::check()) {
|
||||
$account = Auth::user()->account;
|
||||
} elseif ($contactKey = session('contact_key')) {
|
||||
if ($contact = \App\Models\Contact::whereContactKey($contactKey)->first()) {
|
||||
$account = $contact->account;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$account = \App\Models\Account::first();
|
||||
}
|
||||
|
||||
$account = \App\Models\Account::first();
|
||||
|
||||
return $account && $account->hasFeature(FEATURE_WHITE_LABEL);
|
||||
return $account ? $account->hasFeature(FEATURE_WHITE_LABEL) : false;
|
||||
}
|
||||
|
||||
public static function getResllerType()
|
||||
@ -111,6 +124,11 @@ class Utils
|
||||
return isset($_ENV['RESELLER_TYPE']) ? $_ENV['RESELLER_TYPE'] : false;
|
||||
}
|
||||
|
||||
public static function getTermsLink()
|
||||
{
|
||||
return static::isNinja() ? NINJA_WEB_URL.'/terms' : NINJA_WEB_URL.'/self-hosting-the-invoice-ninja-platform';
|
||||
}
|
||||
|
||||
public static function isOAuthEnabled()
|
||||
{
|
||||
$providers = [
|
||||
@ -238,6 +256,8 @@ class Utils
|
||||
$price = PLAN_PRICE_ENTERPRISE_MONTHLY_5;
|
||||
} elseif ($numUsers <= 10) {
|
||||
$price = PLAN_PRICE_ENTERPRISE_MONTHLY_10;
|
||||
} elseif ($numUsers <= 20) {
|
||||
$price = PLAN_PRICE_ENTERPRISE_MONTHLY_20;
|
||||
} else {
|
||||
static::fatalError('Invalid number of users: ' . $numUsers);
|
||||
}
|
||||
@ -256,8 +276,10 @@ class Utils
|
||||
return 1;
|
||||
} elseif ($max <= 5) {
|
||||
return 3;
|
||||
} else {
|
||||
} elseif ($max <= 10) {
|
||||
return 6;
|
||||
} else {
|
||||
return 11;
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,7 +288,7 @@ class Utils
|
||||
return substr($_SERVER['SCRIPT_NAME'], 0, strrpos($_SERVER['SCRIPT_NAME'], '/') + 1);
|
||||
}
|
||||
|
||||
public static function trans($input)
|
||||
public static function trans($input, $module = false)
|
||||
{
|
||||
$data = [];
|
||||
|
||||
@ -274,7 +296,11 @@ class Utils
|
||||
if ($field == 'checkbox') {
|
||||
$data[] = $field;
|
||||
} elseif ($field) {
|
||||
$data[] = trans("texts.$field");
|
||||
if ($module) {
|
||||
$data[] = mtrans($module, $field);
|
||||
} else {
|
||||
$data[] = trans("texts.$field");
|
||||
}
|
||||
} else {
|
||||
$data[] = '';
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ class ActivityListener
|
||||
}
|
||||
|
||||
$backupInvoice = Invoice::with('invoice_items', 'client.account', 'client.contacts')
|
||||
->withArchived()
|
||||
->withTrashed()
|
||||
->find($event->invoice->id);
|
||||
|
||||
$activity = $this->activityRepo->create(
|
||||
@ -200,7 +200,8 @@ class ActivityListener
|
||||
ACTIVITY_TYPE_EMAIL_INVOICE,
|
||||
false,
|
||||
false,
|
||||
$event->invitation
|
||||
$event->invitation,
|
||||
$event->notes
|
||||
);
|
||||
}
|
||||
|
||||
@ -238,7 +239,9 @@ class ActivityListener
|
||||
return;
|
||||
}
|
||||
|
||||
$backupQuote = Invoice::with('invoice_items', 'client.account', 'client.contacts')->find($event->quote->id);
|
||||
$backupQuote = Invoice::with('invoice_items', 'client.account', 'client.contacts')
|
||||
->withTrashed()
|
||||
->find($event->quote->id);
|
||||
|
||||
$activity = $this->activityRepo->create(
|
||||
$event->quote,
|
||||
@ -296,7 +299,8 @@ class ActivityListener
|
||||
ACTIVITY_TYPE_EMAIL_QUOTE,
|
||||
false,
|
||||
false,
|
||||
$event->invitation
|
||||
$event->invitation,
|
||||
$event->notes
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,10 @@ class HandleUserLoggedIn {
|
||||
|
||||
$account->loadLocalizationSettings();
|
||||
|
||||
if (strpos($_SERVER['HTTP_USER_AGENT'], 'iPhone') || strpos($_SERVER['HTTP_USER_AGENT'], 'iPad')) {
|
||||
Session::flash('warning', trans('texts.iphone_app_message', ['link' => link_to(NINJA_IOS_APP_URL, trans('texts.iphone_app'))]));
|
||||
}
|
||||
|
||||
// if they're using Stripe make sure they're using Stripe.js
|
||||
$accountGateway = $account->getGatewayConfig(GATEWAY_STRIPE);
|
||||
if ($accountGateway && ! $accountGateway->getPublishableStripeKey()) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use Utils;
|
||||
use Auth;
|
||||
use App\Models\Activity;
|
||||
use App\Events\InvoiceWasUpdated;
|
||||
use App\Events\InvoiceWasCreated;
|
||||
use App\Events\PaymentWasCreated;
|
||||
@ -69,6 +70,13 @@ class InvoiceListener
|
||||
|
||||
$invoice->updateBalances($adjustment, $partial);
|
||||
$invoice->updatePaidStatus();
|
||||
|
||||
// store a backup of the invoice
|
||||
$activity = Activity::wherePaymentId($payment->id)
|
||||
->whereActivityTypeId(ACTIVITY_TYPE_CREATE_PAYMENT)
|
||||
->first();
|
||||
$activity->json_backup = $invoice->hidePrivateFields()->toJSON();
|
||||
$activity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,46 +1,20 @@
|
||||
<?php namespace App\Listeners;
|
||||
|
||||
use App\Ninja\Mailers\UserMailer;
|
||||
use App\Ninja\Mailers\ContactMailer;
|
||||
use App\Events\InvoiceWasEmailed;
|
||||
use App\Events\QuoteWasEmailed;
|
||||
use App\Events\InvoiceInvitationWasViewed;
|
||||
use App\Events\QuoteInvitationWasViewed;
|
||||
use App\Events\QuoteInvitationWasApproved;
|
||||
use App\Events\PaymentWasCreated;
|
||||
use App\Services\PushService;
|
||||
use App\Jobs\SendPaymentEmail;
|
||||
use App\Jobs\SendNotificationEmail;
|
||||
use App\Jobs\SendPushNotification;
|
||||
|
||||
/**
|
||||
* Class NotificationListener
|
||||
*/
|
||||
class NotificationListener
|
||||
{
|
||||
/**
|
||||
* @var UserMailer
|
||||
*/
|
||||
protected $userMailer;
|
||||
/**
|
||||
* @var ContactMailer
|
||||
*/
|
||||
protected $contactMailer;
|
||||
/**
|
||||
* @var PushService
|
||||
*/
|
||||
protected $pushService;
|
||||
|
||||
/**
|
||||
* NotificationListener constructor.
|
||||
* @param UserMailer $userMailer
|
||||
* @param ContactMailer $contactMailer
|
||||
* @param PushService $pushService
|
||||
*/
|
||||
public function __construct(UserMailer $userMailer, ContactMailer $contactMailer, PushService $pushService)
|
||||
{
|
||||
$this->userMailer = $userMailer;
|
||||
$this->contactMailer = $contactMailer;
|
||||
$this->pushService = $pushService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoice
|
||||
* @param $type
|
||||
@ -52,7 +26,7 @@ class NotificationListener
|
||||
{
|
||||
if ($user->{"notify_{$type}"})
|
||||
{
|
||||
$this->userMailer->sendNotification($user, $invoice, $type, $payment);
|
||||
dispatch(new SendNotificationEmail($user, $invoice, $type, $payment));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -63,7 +37,7 @@ class NotificationListener
|
||||
public function emailedInvoice(InvoiceWasEmailed $event)
|
||||
{
|
||||
$this->sendEmails($event->invoice, 'sent');
|
||||
$this->pushService->sendNotification($event->invoice, 'sent');
|
||||
dispatch(new SendPushNotification($event->invoice, 'sent'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,7 +46,7 @@ class NotificationListener
|
||||
public function emailedQuote(QuoteWasEmailed $event)
|
||||
{
|
||||
$this->sendEmails($event->quote, 'sent');
|
||||
$this->pushService->sendNotification($event->quote, 'sent');
|
||||
dispatch(new SendPushNotification($event->quote, 'sent'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,7 +59,7 @@ class NotificationListener
|
||||
}
|
||||
|
||||
$this->sendEmails($event->invoice, 'viewed');
|
||||
$this->pushService->sendNotification($event->invoice, 'viewed');
|
||||
dispatch(new SendPushNotification($event->invoice, 'viewed'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,7 +72,7 @@ class NotificationListener
|
||||
}
|
||||
|
||||
$this->sendEmails($event->quote, 'viewed');
|
||||
$this->pushService->sendNotification($event->quote, 'viewed');
|
||||
dispatch(new SendPushNotification($event->quote, 'viewed'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,7 +81,7 @@ class NotificationListener
|
||||
public function approvedQuote(QuoteInvitationWasApproved $event)
|
||||
{
|
||||
$this->sendEmails($event->quote, 'approved');
|
||||
$this->pushService->sendNotification($event->quote, 'approved');
|
||||
dispatch(new SendPushNotification($event->quote, 'approved'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -120,10 +94,9 @@ class NotificationListener
|
||||
return;
|
||||
}
|
||||
|
||||
$this->contactMailer->sendPaymentConfirmation($event->payment);
|
||||
$this->sendEmails($event->payment->invoice, 'paid', $event->payment);
|
||||
|
||||
$this->pushService->sendNotification($event->payment->invoice, 'paid');
|
||||
dispatch(new SendPaymentEmail($event->payment));
|
||||
dispatch(new SendPushNotification($event->payment->invoice, 'paid'));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
<?php namespace App\Listeners;
|
||||
|
||||
use App\Events\InvoiceWasDeleted;
|
||||
use App\Events\InvoiceWasUpdated;
|
||||
use App\Events\QuoteWasUpdated;
|
||||
use App\Events\QuoteWasDeleted;
|
||||
use Utils;
|
||||
use App\Models\EntityModel;
|
||||
use App\Events\ClientWasCreated;
|
||||
@ -79,6 +83,42 @@ class SubscriptionListener
|
||||
public function createdExpense(ExpenseWasCreated $event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InvoiceWasUpdated $event
|
||||
*/
|
||||
public function updatedInvoice(InvoiceWasUpdated $event)
|
||||
{
|
||||
$transformer = new InvoiceTransformer($event->invoice->account);
|
||||
$this->checkSubscriptions(EVENT_UPDATE_INVOICE, $event->invoice, $transformer, ENTITY_CLIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InvoiceWasDeleted $event
|
||||
*/
|
||||
public function deletedInvoice(InvoiceWasDeleted $event)
|
||||
{
|
||||
$transformer = new InvoiceTransformer($event->invoice->account);
|
||||
$this->checkSubscriptions(EVENT_DELETE_INVOICE, $event->invoice, $transformer, ENTITY_CLIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QuoteWasUpdated $event
|
||||
*/
|
||||
public function updatedQuote(QuoteWasUpdated $event)
|
||||
{
|
||||
$transformer = new InvoiceTransformer($event->quote->account);
|
||||
$this->checkSubscriptions(EVENT_UPDATE_QUOTE, $event->quote, $transformer, ENTITY_CLIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InvoiceWasDeleted $event
|
||||
*/
|
||||
public function deletedQuote(QuoteWasDeleted $event)
|
||||
{
|
||||
$transformer = new InvoiceTransformer($event->quote->account);
|
||||
$this->checkSubscriptions(EVENT_DELETE_QUOTE, $event->quote, $transformer, ENTITY_CLIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,6 +13,8 @@ use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
use App\Models\Traits\PresentsInvoice;
|
||||
use App\Models\Traits\GeneratesNumbers;
|
||||
use App\Models\Traits\SendsEmails;
|
||||
|
||||
/**
|
||||
* Class Account
|
||||
@ -22,6 +24,8 @@ class Account extends Eloquent
|
||||
use PresentableTrait;
|
||||
use SoftDeletes;
|
||||
use PresentsInvoice;
|
||||
use GeneratesNumbers;
|
||||
use SendsEmails;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
@ -80,6 +84,12 @@ class Account extends Eloquent
|
||||
'show_accept_quote_terms',
|
||||
'require_invoice_signature',
|
||||
'require_quote_signature',
|
||||
'pdf_email_attachment',
|
||||
'document_email_attachment',
|
||||
'email_design_id',
|
||||
'enable_email_markup',
|
||||
'domain_id',
|
||||
'payment_terms',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -103,11 +113,11 @@ class Account extends Eloquent
|
||||
public static $advancedSettings = [
|
||||
ACCOUNT_INVOICE_SETTINGS,
|
||||
ACCOUNT_INVOICE_DESIGN,
|
||||
ACCOUNT_CLIENT_PORTAL,
|
||||
ACCOUNT_EMAIL_SETTINGS,
|
||||
ACCOUNT_TEMPLATES_AND_REMINDERS,
|
||||
ACCOUNT_BANKS,
|
||||
ACCOUNT_CLIENT_PORTAL,
|
||||
ACCOUNT_REPORTS,
|
||||
//ACCOUNT_REPORTS,
|
||||
ACCOUNT_DATA_VISUALIZATIONS,
|
||||
ACCOUNT_API_TOKENS,
|
||||
ACCOUNT_USER_MANAGEMENT,
|
||||
@ -466,6 +476,17 @@ class Account extends Eloquent
|
||||
return $this->date_format ? $this->date_format->format : DEFAULT_DATE_FORMAT;
|
||||
}
|
||||
|
||||
|
||||
public function getSampleLink()
|
||||
{
|
||||
$invitation = new Invitation();
|
||||
$invitation->account = $this;
|
||||
$invitation->invitation_key = '...';
|
||||
|
||||
return $invitation->getLink();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $amount
|
||||
* @param null $client
|
||||
@ -862,7 +883,7 @@ class Account extends Eloquent
|
||||
if ($this->hasClientNumberPattern($invoice) && !$clientId) {
|
||||
// do nothing, we don't yet know the value
|
||||
} elseif ( ! $invoice->invoice_number) {
|
||||
$invoice->invoice_number = $this->getNextInvoiceNumber($invoice);
|
||||
$invoice->invoice_number = $this->getNextNumber($invoice);
|
||||
}
|
||||
}
|
||||
|
||||
@ -874,191 +895,6 @@ class Account extends Eloquent
|
||||
return $invoice;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoice_type_id
|
||||
* @return string
|
||||
*/
|
||||
public function getNumberPrefix($invoice_type_id)
|
||||
{
|
||||
if ( ! $this->hasFeature(FEATURE_INVOICE_SETTINGS)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return ($invoice_type_id == INVOICE_TYPE_QUOTE ? $this->quote_number_prefix : $this->invoice_number_prefix) ?: '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoice_type_id
|
||||
* @return bool
|
||||
*/
|
||||
public function hasNumberPattern($invoice_type_id)
|
||||
{
|
||||
if ( ! $this->hasFeature(FEATURE_INVOICE_SETTINGS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $invoice_type_id == INVOICE_TYPE_QUOTE ? ($this->quote_number_pattern ? true : false) : ($this->invoice_number_pattern ? true : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoice
|
||||
* @return string
|
||||
*/
|
||||
public function hasClientNumberPattern($invoice)
|
||||
{
|
||||
$pattern = $invoice->invoice_type_id == INVOICE_TYPE_QUOTE ? $this->quote_number_pattern : $this->invoice_number_pattern;
|
||||
|
||||
return strstr($pattern, '$custom');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoice
|
||||
* @return bool|mixed
|
||||
*/
|
||||
public function getNumberPattern($invoice)
|
||||
{
|
||||
$pattern = $invoice->invoice_type_id == INVOICE_TYPE_QUOTE ? $this->quote_number_pattern : $this->invoice_number_pattern;
|
||||
|
||||
if (!$pattern) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$search = ['{$year}'];
|
||||
$replace = [date('Y')];
|
||||
|
||||
$search[] = '{$counter}';
|
||||
$replace[] = str_pad($this->getCounter($invoice->invoice_type_id), $this->invoice_number_padding, '0', STR_PAD_LEFT);
|
||||
|
||||
if (strstr($pattern, '{$userId}')) {
|
||||
$search[] = '{$userId}';
|
||||
$replace[] = str_pad(($invoice->user->public_id + 1), 2, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
$matches = false;
|
||||
preg_match('/{\$date:(.*?)}/', $pattern, $matches);
|
||||
if (count($matches) > 1) {
|
||||
$format = $matches[1];
|
||||
$search[] = $matches[0];
|
||||
$replace[] = str_replace($format, date($format), $matches[1]);
|
||||
}
|
||||
|
||||
$pattern = str_replace($search, $replace, $pattern);
|
||||
|
||||
if ($invoice->client_id) {
|
||||
$pattern = $this->getClientInvoiceNumber($pattern, $invoice);
|
||||
}
|
||||
|
||||
return $pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $pattern
|
||||
* @param $invoice
|
||||
* @return mixed
|
||||
*/
|
||||
private function getClientInvoiceNumber($pattern, $invoice)
|
||||
{
|
||||
if (!$invoice->client) {
|
||||
return $pattern;
|
||||
}
|
||||
|
||||
$search = [
|
||||
'{$custom1}',
|
||||
'{$custom2}',
|
||||
];
|
||||
|
||||
$replace = [
|
||||
$invoice->client->custom_value1,
|
||||
$invoice->client->custom_value2,
|
||||
];
|
||||
|
||||
return str_replace($search, $replace, $pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoice_type_id
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCounter($invoice_type_id)
|
||||
{
|
||||
return $invoice_type_id == INVOICE_TYPE_QUOTE && !$this->share_counter ? $this->quote_number_counter : $this->invoice_number_counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entityType
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function previewNextInvoiceNumber($entityType = ENTITY_INVOICE)
|
||||
{
|
||||
$invoice = $this->createInvoice($entityType);
|
||||
return $this->getNextInvoiceNumber($invoice);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoice
|
||||
* @param bool $validateUnique
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function getNextInvoiceNumber($invoice, $validateUnique = true)
|
||||
{
|
||||
if ($this->hasNumberPattern($invoice->invoice_type_id)) {
|
||||
$number = $this->getNumberPattern($invoice);
|
||||
} else {
|
||||
$counter = $this->getCounter($invoice->invoice_type_id);
|
||||
$prefix = $this->getNumberPrefix($invoice->invoice_type_id);
|
||||
$counterOffset = 0;
|
||||
$check = false;
|
||||
|
||||
// confirm the invoice number isn't already taken
|
||||
do {
|
||||
$number = $prefix . str_pad($counter, $this->invoice_number_padding, '0', STR_PAD_LEFT);
|
||||
if ($validateUnique) {
|
||||
$check = Invoice::scope(false, $this->id)->whereInvoiceNumber($number)->withTrashed()->first();
|
||||
$counter++;
|
||||
$counterOffset++;
|
||||
}
|
||||
} while ($check);
|
||||
|
||||
// update the invoice counter to be caught up
|
||||
if ($counterOffset > 1) {
|
||||
if ($invoice->isType(INVOICE_TYPE_QUOTE)) {
|
||||
if ( ! $this->share_counter) {
|
||||
$this->quote_number_counter += $counterOffset - 1;
|
||||
}
|
||||
} else {
|
||||
$this->invoice_number_counter += $counterOffset - 1;
|
||||
}
|
||||
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
if ($invoice->recurring_invoice_id) {
|
||||
$number = $this->recurring_invoice_number_prefix . $number;
|
||||
}
|
||||
|
||||
return $number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $invoice
|
||||
*/
|
||||
public function incrementCounter($invoice)
|
||||
{
|
||||
// if they didn't use the counter don't increment it
|
||||
if ($invoice->invoice_number != $this->getNextInvoiceNumber($invoice, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($invoice->isType(INVOICE_TYPE_QUOTE) && !$this->share_counter) {
|
||||
$this->quote_number_counter += 1;
|
||||
} else {
|
||||
$this->invoice_number_counter += 1;
|
||||
}
|
||||
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $client
|
||||
*/
|
||||
@ -1107,6 +943,10 @@ class Account extends Eloquent
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->company->trial_started && $this->company->trial_started != '0000-00-00') {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->company->trial_plan = $plan;
|
||||
$this->company->trial_started = date_create()->format('Y-m-d');
|
||||
$this->company->save();
|
||||
@ -1166,7 +1006,6 @@ class Account extends Eloquent
|
||||
return false;
|
||||
}
|
||||
// Fallthrough
|
||||
case FEATURE_CLIENT_PORTAL_CSS:
|
||||
case FEATURE_REMOVE_CREATED_BY:
|
||||
return !empty($planDetails);// A plan is required even for self-hosted users
|
||||
|
||||
@ -1342,31 +1181,6 @@ class Account extends Eloquent
|
||||
return $plan_details && $plan_details['trial'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $plan
|
||||
* @return array|bool
|
||||
*/
|
||||
public function isEligibleForTrial($plan = null)
|
||||
{
|
||||
if (!$this->company->trial_plan) {
|
||||
if ($plan) {
|
||||
return $plan == PLAN_PRO || $plan == PLAN_ENTERPRISE;
|
||||
} else {
|
||||
return [PLAN_PRO, PLAN_ENTERPRISE];
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->company->trial_plan == PLAN_PRO) {
|
||||
if ($plan) {
|
||||
return $plan != PLAN_PRO;
|
||||
} else {
|
||||
return [PLAN_ENTERPRISE];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
@ -1709,8 +1523,8 @@ class Account extends Eloquent
|
||||
*/
|
||||
public function showCustomField($field, $entity = false)
|
||||
{
|
||||
if ($this->hasFeature(FEATURE_INVOICE_SETTINGS)) {
|
||||
return $this->$field ? true : false;
|
||||
if ($this->hasFeature(FEATURE_INVOICE_SETTINGS) && $this->$field) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$entity) {
|
||||
@ -1753,9 +1567,7 @@ class Account extends Eloquent
|
||||
if ($headerFont != $bodyFont) {
|
||||
$css .= 'h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{'.$headerFont.'}';
|
||||
}
|
||||
}
|
||||
if ($this->hasFeature(FEATURE_CLIENT_PORTAL_CSS)) {
|
||||
// For self-hosted users, a white-label license is required for custom CSS
|
||||
|
||||
$css .= $this->client_view_css;
|
||||
}
|
||||
|
||||
@ -1882,7 +1694,7 @@ class Account extends Eloquent
|
||||
return $this->enabled_modules & static::$modules[$entityType];
|
||||
}
|
||||
|
||||
public function showAuthenticatePanel($invoice)
|
||||
public function requiresAuthorization($invoice)
|
||||
{
|
||||
return $this->showAcceptTerms($invoice) || $this->showSignature($invoice);
|
||||
}
|
||||
@ -1904,6 +1716,22 @@ class Account extends Eloquent
|
||||
|
||||
return $invoice->isQuote() ? $this->require_quote_signature : $this->require_invoice_signature;
|
||||
}
|
||||
|
||||
public function emailMarkupEnabled()
|
||||
{
|
||||
if ( ! Utils::isNinja()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->enable_email_markup;
|
||||
}
|
||||
|
||||
public function defaultDueDate()
|
||||
{
|
||||
$numDays = $this->payment_terms == -1 ? 0 : $this->payment_terms;
|
||||
|
||||
return Carbon::now($this->getTimezone())->addDays($numDays)->format('Y-m-d');
|
||||
}
|
||||
}
|
||||
|
||||
Account::updated(function ($account)
|
||||
|
@ -275,6 +275,12 @@ class Client extends EntityModel
|
||||
} else {
|
||||
$contact = Contact::createNew();
|
||||
$contact->send_invoice = true;
|
||||
|
||||
if (isset($data['contact_key']) && $this->account->account_key == env('NINJA_LICENSE_ACCOUNT_KEY')) {
|
||||
$contact->contact_key = $data['contact_key'];
|
||||
} else {
|
||||
$contact->contact_key = str_random(RANDOM_KEY_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
if (Utils::hasFeature(FEATURE_CLIENT_PORTAL_PASSWORD) && $this->account->enable_portal_password){
|
||||
@ -379,6 +385,14 @@ class Client extends EntityModel
|
||||
return ENTITY_CLIENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function showMap()
|
||||
{
|
||||
return $this->hasAddress() && env('GOOGLE_MAPS_ENABLED') !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
@ -521,6 +535,7 @@ class Client extends EntityModel
|
||||
|
||||
Client::creating(function ($client) {
|
||||
$client->setNullValues();
|
||||
$client->account->incrementCounter($client);
|
||||
});
|
||||
|
||||
Client::updating(function ($client) {
|
||||
|
@ -56,13 +56,17 @@ class Company extends Eloquent
|
||||
// handle promos and discounts
|
||||
public function hasActiveDiscount(Carbon $date = null)
|
||||
{
|
||||
if ( ! $this->discount) {
|
||||
if ( ! $this->discount || ! $this->discount_expires) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$date = $date ?: Carbon::today();
|
||||
|
||||
return $this->discount_expires && $this->discount_expires->gt($date);
|
||||
if ($this->plan_term == PLAN_TERM_MONTHLY) {
|
||||
return $this->discount_expires->gt($date);
|
||||
} else {
|
||||
return $this->discount_expires->subMonths(11)->gt($date);
|
||||
}
|
||||
}
|
||||
|
||||
public function discountedPrice($price)
|
||||
@ -74,6 +78,29 @@ class Company extends Eloquent
|
||||
return $price - ($price * $this->discount);
|
||||
}
|
||||
|
||||
public function daysUntilPlanExpires()
|
||||
{
|
||||
if ( ! $this->hasActivePlan()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Carbon::parse($this->plan_expires)->diffInDays(Carbon::today());
|
||||
}
|
||||
|
||||
public function hasActivePlan()
|
||||
{
|
||||
return Carbon::parse($this->plan_expires) >= Carbon::today();
|
||||
}
|
||||
|
||||
public function hasExpiredPlan($plan)
|
||||
{
|
||||
if ($this->plan != $plan) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Carbon::parse($this->plan_expires) < Carbon::today();
|
||||
}
|
||||
|
||||
public function hasEarnedPromo()
|
||||
{
|
||||
if ( ! Utils::isNinjaProd() || Utils::isPro()) {
|
||||
|
@ -128,7 +128,7 @@ class Contact extends EntityModel implements AuthenticatableContract, CanResetPa
|
||||
public function getFullName()
|
||||
{
|
||||
if ($this->first_name || $this->last_name) {
|
||||
return $this->first_name.' '.$this->last_name;
|
||||
return trim($this->first_name.' '.$this->last_name);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
@ -289,6 +289,7 @@ class EntityModel extends Eloquent
|
||||
'vendors' => 'building',
|
||||
'settings' => 'cog',
|
||||
'self-update' => 'download',
|
||||
'reports' => 'th-list',
|
||||
];
|
||||
|
||||
return array_get($icons, $entityType);
|
||||
@ -335,4 +336,15 @@ class EntityModel extends Eloquent
|
||||
|
||||
return $class::getStatuses($entityType);
|
||||
}
|
||||
|
||||
public function statusClass()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function statusLabel()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -221,6 +221,53 @@ class Expense extends EntityModel
|
||||
|
||||
return $statuses;
|
||||
}
|
||||
|
||||
public static function calcStatusLabel($shouldBeInvoiced, $invoiceId, $balance)
|
||||
{
|
||||
if ($invoiceId) {
|
||||
if (floatval($balance) > 0) {
|
||||
$label = 'invoiced';
|
||||
} else {
|
||||
$label = 'paid';
|
||||
}
|
||||
} elseif ($shouldBeInvoiced) {
|
||||
$label = 'pending';
|
||||
} else {
|
||||
$label = 'logged';
|
||||
}
|
||||
|
||||
return trans("texts.{$label}");
|
||||
}
|
||||
|
||||
public static function calcStatusClass($shouldBeInvoiced, $invoiceId, $balance)
|
||||
{
|
||||
if ($invoiceId) {
|
||||
if (floatval($balance) > 0) {
|
||||
return 'default';
|
||||
} else {
|
||||
return 'success';
|
||||
}
|
||||
} elseif ($shouldBeInvoiced) {
|
||||
return 'warning';
|
||||
} else {
|
||||
return 'primary';
|
||||
}
|
||||
}
|
||||
|
||||
public function statusClass()
|
||||
{
|
||||
$balance = $this->invoice ? $this->invoice->balance : 0;
|
||||
|
||||
return static::calcStatusClass($this->should_be_invoiced, $this->invoice_id, $balance);
|
||||
}
|
||||
|
||||
public function statusLabel()
|
||||
{
|
||||
$balance = $this->invoice ? $this->invoice->balance : 0;
|
||||
|
||||
return static::calcStatusLabel($this->should_be_invoiced, $this->invoice_id, $balance);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Expense::creating(function ($expense) {
|
||||
|
@ -68,14 +68,19 @@ class Invitation extends EntityModel
|
||||
$this->load('account');
|
||||
}
|
||||
|
||||
$account = $this->account;
|
||||
$iframe_url = $account->iframe_url;
|
||||
$url = trim(SITE_URL, '/');
|
||||
$iframe_url = $this->account->iframe_url;
|
||||
|
||||
if ($this->account->hasFeature(FEATURE_CUSTOM_URL)) {
|
||||
if ($account->hasFeature(FEATURE_CUSTOM_URL)) {
|
||||
if (Utils::isNinjaProd()) {
|
||||
$url = $account->present()->clientPortalLink();
|
||||
}
|
||||
|
||||
if ($iframe_url && !$forceOnsite) {
|
||||
return "{$iframe_url}?{$this->invitation_key}";
|
||||
} elseif ($this->account->subdomain) {
|
||||
$url = Utils::replaceSubdomain($url, $this->account->subdomain);
|
||||
$url = Utils::replaceSubdomain($url, $account->subdomain);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ use App\Events\InvoiceWasCreated;
|
||||
use App\Events\InvoiceWasUpdated;
|
||||
use App\Events\InvoiceInvitationWasEmailed;
|
||||
use App\Events\QuoteInvitationWasEmailed;
|
||||
use App\Libraries\CurlUtils;
|
||||
|
||||
/**
|
||||
* Class Invoice
|
||||
@ -64,10 +65,22 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
'date:',
|
||||
];
|
||||
|
||||
public static $statusClasses = [
|
||||
INVOICE_STATUS_SENT => 'info',
|
||||
INVOICE_STATUS_VIEWED => 'warning',
|
||||
INVOICE_STATUS_APPROVED => 'success',
|
||||
INVOICE_STATUS_PARTIAL => 'primary',
|
||||
INVOICE_STATUS_PAID => 'success',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static $fieldInvoiceNumber = 'invoice_number';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static $fieldPONumber = 'po_number';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
@ -101,6 +114,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
return [
|
||||
Client::$fieldName,
|
||||
Invoice::$fieldInvoiceNumber,
|
||||
Invoice::$fieldPONumber,
|
||||
Invoice::$fieldInvoiceDate,
|
||||
Invoice::$fieldDueDate,
|
||||
Invoice::$fieldAmount,
|
||||
@ -118,9 +132,11 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
return [
|
||||
'number^po' => 'invoice_number',
|
||||
'amount' => 'amount',
|
||||
'organization' => 'name',
|
||||
'client|organization' => 'name',
|
||||
'paid^date' => 'paid',
|
||||
'invoice_date|create_date' => 'invoice_date',
|
||||
'invoice date|create date' => 'invoice_date',
|
||||
'po number' => 'po_number',
|
||||
'due date' => 'due_date',
|
||||
'terms' => 'terms',
|
||||
'notes' => 'notes',
|
||||
];
|
||||
@ -181,31 +197,38 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
return floatval($this->amount) - floatval($this->getOriginal('amount'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isChanged()
|
||||
{
|
||||
if ($this->getRawAdjustment() != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ([
|
||||
'invoice_number',
|
||||
'po_number',
|
||||
'invoice_date',
|
||||
'due_date',
|
||||
'terms',
|
||||
'public_notes',
|
||||
'invoice_footer',
|
||||
'partial',
|
||||
] as $field) {
|
||||
if ($this->$field != $this->getOriginal($field)) {
|
||||
if (Utils::isNinja()) {
|
||||
if ($this->getRawAdjustment() != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
foreach ([
|
||||
'invoice_number',
|
||||
'po_number',
|
||||
'invoice_date',
|
||||
'due_date',
|
||||
'terms',
|
||||
'public_notes',
|
||||
'invoice_footer',
|
||||
'partial',
|
||||
] as $field) {
|
||||
if ($this->$field != $this->getOriginal($field)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
$dirty = $this->getDirty();
|
||||
|
||||
unset($dirty['invoice_status_id']);
|
||||
unset($dirty['client_enable_auto_bill']);
|
||||
unset($dirty['quote_invoice_id']);
|
||||
|
||||
return count($dirty) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -410,17 +433,36 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
return $this->isType(INVOICE_TYPE_STANDARD) && ! $this->is_recurring;
|
||||
}
|
||||
|
||||
public function markSentIfUnsent()
|
||||
{
|
||||
if ( ! $this->isSent()) {
|
||||
$this->markSent();
|
||||
}
|
||||
}
|
||||
|
||||
public function markSent()
|
||||
{
|
||||
if ( ! $this->isSent()) {
|
||||
$this->invoice_status_id = INVOICE_STATUS_SENT;
|
||||
}
|
||||
|
||||
$this->is_public = true;
|
||||
$this->save();
|
||||
|
||||
$this->markInvitationsSent();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $notify
|
||||
*/
|
||||
public function markInvitationsSent($notify = false)
|
||||
public function markInvitationsSent($notify = false, $reminder = false)
|
||||
{
|
||||
if ( ! $this->relationLoaded('invitations')) {
|
||||
$this->load('invitations');
|
||||
}
|
||||
|
||||
foreach ($this->invitations as $invitation) {
|
||||
$this->markInvitationSent($invitation, false, $notify);
|
||||
$this->markInvitationSent($invitation, false, $notify, $reminder);
|
||||
}
|
||||
}
|
||||
|
||||
@ -444,9 +486,9 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
* @param bool $messageId
|
||||
* @param bool $notify
|
||||
*/
|
||||
public function markInvitationSent($invitation, $messageId = false, $notify = true)
|
||||
public function markInvitationSent($invitation, $messageId = false, $notify = true, $notes = false)
|
||||
{
|
||||
if (!$this->isSent()) {
|
||||
if ( ! $this->isSent()) {
|
||||
$this->invoice_status_id = INVOICE_STATUS_SENT;
|
||||
$this->save();
|
||||
}
|
||||
@ -460,9 +502,9 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
}
|
||||
|
||||
if ($this->isType(INVOICE_TYPE_QUOTE)) {
|
||||
event(new QuoteInvitationWasEmailed($invitation));
|
||||
event(new QuoteInvitationWasEmailed($invitation, $notes));
|
||||
} else {
|
||||
event(new InvoiceInvitationWasEmailed($invitation));
|
||||
event(new InvoiceInvitationWasEmailed($invitation, $notes));
|
||||
}
|
||||
}
|
||||
|
||||
@ -550,7 +592,58 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
|
||||
public function canBePaid()
|
||||
{
|
||||
return floatval($this->balance) > 0 && ! $this->is_deleted && $this->isInvoice() && $this->is_public;
|
||||
return floatval($this->balance) > 0 && ! $this->is_deleted && $this->isInvoice();
|
||||
}
|
||||
|
||||
public static function calcStatusLabel($status, $class, $entityType, $quoteInvoiceId)
|
||||
{
|
||||
if ($quoteInvoiceId) {
|
||||
$label = 'converted';
|
||||
} else if ($class == 'danger') {
|
||||
$label = $entityType == ENTITY_INVOICE ? 'overdue' : 'expired';
|
||||
} else {
|
||||
$label = 'status_' . strtolower($status);
|
||||
}
|
||||
|
||||
return trans("texts.{$label}");
|
||||
}
|
||||
|
||||
public static function calcStatusClass($statusId, $balance, $dueDate)
|
||||
{
|
||||
if (static::calcIsOverdue($balance, $dueDate)) {
|
||||
return 'danger';
|
||||
}
|
||||
|
||||
if (isset(static::$statusClasses[$statusId])) {
|
||||
return static::$statusClasses[$statusId];
|
||||
}
|
||||
|
||||
return 'default';
|
||||
}
|
||||
|
||||
public static function calcIsOverdue($balance, $dueDate)
|
||||
{
|
||||
if ( ! Utils::parseFloat($balance) > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $dueDate || $dueDate == '0000-00-00') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// it isn't considered overdue until the end of the day
|
||||
return time() > (strtotime($dueDate) + (60*60*24));
|
||||
}
|
||||
|
||||
public function statusClass()
|
||||
{
|
||||
return static::calcStatusClass($this->invoice_status_id, $this->balance, $this->due_date);
|
||||
}
|
||||
|
||||
public function statusLabel()
|
||||
{
|
||||
return static::calcStatusLabel($this->invoice_status->name, $this->statusClass(), $this->getEntityType(), $this->quote_invoice_id);
|
||||
>>>>>>> release-3.0.0
|
||||
}
|
||||
|
||||
/**
|
||||
@ -601,7 +694,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
*/
|
||||
public function isSent()
|
||||
{
|
||||
return $this->invoice_status_id >= INVOICE_STATUS_SENT && $this->is_public;
|
||||
return $this->invoice_status_id >= INVOICE_STATUS_SENT && $this->getOriginal('is_public');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -633,11 +726,7 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
*/
|
||||
public function isOverdue()
|
||||
{
|
||||
if ( ! $this->due_date) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return time() > strtotime($this->due_date);
|
||||
return static::calcIsOverdue($this->balance, $this->due_date);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1099,21 +1188,23 @@ class Invoice extends EntityModel implements BalanceAffecting
|
||||
*/
|
||||
public function getPDFString()
|
||||
{
|
||||
if (!env('PHANTOMJS_CLOUD_KEY')) {
|
||||
if ( ! env('PHANTOMJS_CLOUD_KEY') && ! env('PHANTOMJS_BIN_PATH')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$invitation = $this->invitations[0];
|
||||
$link = $invitation->getLink('view', true);
|
||||
$key = env('PHANTOMJS_CLOUD_KEY');
|
||||
|
||||
if (Utils::isNinjaDev()) {
|
||||
$link = env('TEST_LINK');
|
||||
if (env('PHANTOMJS_BIN_PATH')) {
|
||||
$pdfString = CurlUtils::phantom('GET', $link . '?phantomjs=true');
|
||||
} elseif ($key = env('PHANTOMJS_CLOUD_KEY')) {
|
||||
if (Utils::isNinjaDev()) {
|
||||
$link = env('TEST_LINK');
|
||||
}
|
||||
$url = "http://api.phantomjscloud.com/api/browser/v2/{$key}/?request=%7Burl:%22{$link}?phantomjs=true%22,renderType:%22html%22%7D";
|
||||
$pdfString = CurlUtils::get($url);
|
||||
}
|
||||
|
||||
$url = "http://api.phantomjscloud.com/api/browser/v2/{$key}/?request=%7Burl:%22{$link}?phantomjs=true%22,renderType:%22html%22%7D";
|
||||
|
||||
$pdfString = file_get_contents($url);
|
||||
$pdfString = strip_tags($pdfString);
|
||||
|
||||
if ( ! $pdfString || strlen($pdfString) < 200) {
|
||||
|
@ -17,6 +17,15 @@ class Payment extends EntityModel
|
||||
use PresentableTrait;
|
||||
use SoftDeletes;
|
||||
|
||||
public static $statusClasses = [
|
||||
PAYMENT_STATUS_PENDING => 'info',
|
||||
PAYMENT_STATUS_COMPLETED => 'success',
|
||||
PAYMENT_STATUS_FAILED => 'danger',
|
||||
PAYMENT_STATUS_PARTIALLY_REFUNDED => 'primary',
|
||||
PAYMENT_STATUS_VOIDED => 'default',
|
||||
PAYMENT_STATUS_REFUNDED => 'default',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@ -302,6 +311,44 @@ class Payment extends EntityModel
|
||||
{
|
||||
return $value ? str_pad($value, 4, '0', STR_PAD_LEFT) : null;
|
||||
}
|
||||
|
||||
public static function calcStatusLabel($statusId, $statusName, $amount)
|
||||
{
|
||||
if ($statusId == PAYMENT_STATUS_PARTIALLY_REFUNDED) {
|
||||
return trans('texts.status_partially_refunded_amount', [
|
||||
'amount' => $amount,
|
||||
]);
|
||||
} else {
|
||||
return trans('texts.status_' . strtolower($statusName));
|
||||
}
|
||||
}
|
||||
|
||||
public static function calcStatusClass($statusId)
|
||||
{
|
||||
return static::$statusClasses[$statusId];
|
||||
}
|
||||
|
||||
|
||||
public function statusClass()
|
||||
{
|
||||
return static::calcStatusClass($this->payment_status_id);
|
||||
}
|
||||
|
||||
public function statusLabel()
|
||||
{
|
||||
$amount = $this->account->formatMoney($this->refunded, $this->client);
|
||||
return static::calcStatusLabel($this->payment_status_id, $this->payment_status->name, $amount);
|
||||
}
|
||||
|
||||
public function invoiceJsonBackup()
|
||||
{
|
||||
$activity = Activity::wherePaymentId($this->id)
|
||||
->whereActivityTypeId(ACTIVITY_TYPE_CREATE_PAYMENT)
|
||||
->get(['json_backup'])
|
||||
->first();
|
||||
|
||||
return $activity->json_backup;
|
||||
}
|
||||
}
|
||||
|
||||
Payment::creating(function ($payment) {
|
||||
|
@ -215,8 +215,64 @@ class Task extends EntityModel
|
||||
return $statuses;
|
||||
}
|
||||
|
||||
}
|
||||
public static function calcStatusLabel($isRunning, $balance, $invoiceNumber)
|
||||
{
|
||||
if ($invoiceNumber) {
|
||||
if (floatval($balance) > 0) {
|
||||
$label = 'invoiced';
|
||||
} else {
|
||||
$label = 'paid';
|
||||
}
|
||||
} elseif ($isRunning) {
|
||||
$label = 'running';
|
||||
} else {
|
||||
$label = 'logged';
|
||||
}
|
||||
|
||||
return trans("texts.{$label}");
|
||||
}
|
||||
|
||||
public static function calcStatusClass($isRunning, $balance, $invoiceNumber)
|
||||
{
|
||||
if ($invoiceNumber) {
|
||||
if (floatval($balance)) {
|
||||
return 'default';
|
||||
} else {
|
||||
return 'success';
|
||||
}
|
||||
} elseif ($isRunning) {
|
||||
return 'primary';
|
||||
} else {
|
||||
return 'warning';
|
||||
}
|
||||
}
|
||||
|
||||
public function statusClass()
|
||||
{
|
||||
if ($this->invoice) {
|
||||
$balance = $this->invoice->balance;
|
||||
$invoiceNumber = $this->invoice->invoice_number;
|
||||
} else {
|
||||
$balance = 0;
|
||||
$invoiceNumber = false;
|
||||
}
|
||||
|
||||
return static::calcStatusClass($this->is_running, $balance, $invoiceNumber);
|
||||
}
|
||||
|
||||
public function statusLabel()
|
||||
{
|
||||
if ($this->invoice) {
|
||||
$balance = $this->invoice->balance;
|
||||
$invoiceNumber = $this->invoice->invoice_number;
|
||||
} else {
|
||||
$balance = 0;
|
||||
$invoiceNumber = false;
|
||||
}
|
||||
|
||||
return static::calcStatusLabel($this->is_running, $balance, $invoiceNumber);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Task::created(function ($task) {
|
||||
|
@ -18,7 +18,8 @@ class TaxRate extends EntityModel
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'rate'
|
||||
'rate',
|
||||
'is_inclusive',
|
||||
];
|
||||
|
||||
/**
|
||||
|
236
app/Models/Traits/GeneratesNumbers.php
Normal file
236
app/Models/Traits/GeneratesNumbers.php
Normal file
@ -0,0 +1,236 @@
|
||||
<?php namespace App\Models\Traits;
|
||||
|
||||
use Auth;
|
||||
use Carbon;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Client;
|
||||
|
||||
/**
|
||||
* Class GeneratesNumbers
|
||||
*/
|
||||
trait GeneratesNumbers
|
||||
{
|
||||
/**
|
||||
* @param $entity
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function getNextNumber($entity = false)
|
||||
{
|
||||
$entity = $entity ?: new Client();
|
||||
$entityType = $entity->getEntityType();
|
||||
|
||||
$counter = $this->getCounter($entityType);
|
||||
$prefix = $this->getNumberPrefix($entityType);
|
||||
$counterOffset = 0;
|
||||
$check = false;
|
||||
|
||||
if ($entityType == ENTITY_CLIENT && ! $this->clientNumbersEnabled()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// confirm the invoice number isn't already taken
|
||||
do {
|
||||
if ($this->hasNumberPattern($entityType)) {
|
||||
$number = $this->applyNumberPattern($entity, $counter);
|
||||
} else {
|
||||
$number = $prefix . str_pad($counter, $this->invoice_number_padding, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
if ($entity->isEntityType(ENTITY_CLIENT)) {
|
||||
$check = Client::scope(false, $this->id)->whereIdNumber($number)->withTrashed()->first();
|
||||
} else {
|
||||
$check = Invoice::scope(false, $this->id)->whereInvoiceNumber($number)->withTrashed()->first();
|
||||
}
|
||||
$counter++;
|
||||
$counterOffset++;
|
||||
} while ($check);
|
||||
|
||||
// update the counter to be caught up
|
||||
if ($counterOffset > 1) {
|
||||
if ($entity->isEntityType(ENTITY_CLIENT)) {
|
||||
if ($this->clientNumbersEnabled()) {
|
||||
$this->client_number_counter += $counterOffset - 1;
|
||||
$this->save();
|
||||
}
|
||||
} elseif ($entity->isType(INVOICE_TYPE_QUOTE)) {
|
||||
if ( ! $this->share_counter) {
|
||||
$this->quote_number_counter += $counterOffset - 1;
|
||||
$this->save();
|
||||
}
|
||||
} else {
|
||||
$this->invoice_number_counter += $counterOffset - 1;
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->recurring_invoice_id) {
|
||||
$number = $this->recurring_invoice_number_prefix . $number;
|
||||
}
|
||||
|
||||
return $number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entityType
|
||||
* @return string
|
||||
*/
|
||||
public function getNumberPrefix($entityType)
|
||||
{
|
||||
if ( ! $this->hasFeature(FEATURE_INVOICE_SETTINGS)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$field = "{$entityType}_number_prefix";
|
||||
return $this->$field ?: '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entityType
|
||||
* @return bool
|
||||
*/
|
||||
public function getNumberPattern($entityType)
|
||||
{
|
||||
if ( ! $this->hasFeature(FEATURE_INVOICE_SETTINGS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$field = "{$entityType}_number_pattern";
|
||||
return $this->$field;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entityType
|
||||
* @return bool
|
||||
*/
|
||||
public function hasNumberPattern($entityType)
|
||||
{
|
||||
return $this->getNumberPattern($entityType) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entityType
|
||||
* @return string
|
||||
*/
|
||||
public function hasClientNumberPattern($invoice)
|
||||
{
|
||||
$pattern = $invoice->invoice_type_id == INVOICE_TYPE_QUOTE ? $this->quote_number_pattern : $this->invoice_number_pattern;
|
||||
|
||||
return strstr($pattern, '$custom');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entity
|
||||
* @return bool|mixed
|
||||
*/
|
||||
public function applyNumberPattern($entity, $counter = 0)
|
||||
{
|
||||
$entityType = $entity->getEntityType();
|
||||
$counter = $counter ?: $this->getCounter($entityType);
|
||||
$pattern = $this->getNumberPattern($entityType);
|
||||
|
||||
if (!$pattern) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$search = ['{$year}'];
|
||||
$replace = [date('Y')];
|
||||
|
||||
$search[] = '{$counter}';
|
||||
$replace[] = str_pad($counter, $this->invoice_number_padding, '0', STR_PAD_LEFT);
|
||||
|
||||
if (strstr($pattern, '{$userId}')) {
|
||||
$userId = $entity->user ? $entity->user->public_id : (Auth::check() ? Auth::user()->public_id : 0);
|
||||
$search[] = '{$userId}';
|
||||
$replace[] = str_pad(($userId + 1), 2, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
$matches = false;
|
||||
preg_match('/{\$date:(.*?)}/', $pattern, $matches);
|
||||
if (count($matches) > 1) {
|
||||
$format = $matches[1];
|
||||
$search[] = $matches[0];
|
||||
$date = Carbon::now(session(SESSION_TIMEZONE, DEFAULT_TIMEZONE))->format($format);
|
||||
$replace[] = str_replace($format, $date, $matches[1]);
|
||||
}
|
||||
|
||||
$pattern = str_replace($search, $replace, $pattern);
|
||||
|
||||
if ($entity->client_id) {
|
||||
$pattern = $this->getClientInvoiceNumber($pattern, $entity);
|
||||
}
|
||||
|
||||
return $pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $pattern
|
||||
* @param $invoice
|
||||
* @return mixed
|
||||
*/
|
||||
private function getClientInvoiceNumber($pattern, $invoice)
|
||||
{
|
||||
if (!$invoice->client) {
|
||||
return $pattern;
|
||||
}
|
||||
|
||||
$search = [
|
||||
'{$custom1}',
|
||||
'{$custom2}',
|
||||
];
|
||||
|
||||
$replace = [
|
||||
$invoice->client->custom_value1,
|
||||
$invoice->client->custom_value2,
|
||||
];
|
||||
|
||||
return str_replace($search, $replace, $pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entityType
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCounter($entityType)
|
||||
{
|
||||
if ($entityType == ENTITY_CLIENT) {
|
||||
return $this->client_number_counter;
|
||||
} elseif ($entityType == ENTITY_QUOTE && ! $this->share_counter) {
|
||||
return $this->quote_number_counter;
|
||||
} else {
|
||||
return $this->invoice_number_counter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entityType
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function previewNextInvoiceNumber($entityType = ENTITY_INVOICE)
|
||||
{
|
||||
$invoice = $this->createInvoice($entityType);
|
||||
return $this->getNextNumber($invoice);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entity
|
||||
*/
|
||||
public function incrementCounter($entity)
|
||||
{
|
||||
if ($entity->isEntityType(ENTITY_CLIENT)) {
|
||||
if ($this->client_number_counter) {
|
||||
$this->client_number_counter += 1;
|
||||
}
|
||||
} elseif ($entity->isType(INVOICE_TYPE_QUOTE) && ! $this->share_counter) {
|
||||
$this->quote_number_counter += 1;
|
||||
} else {
|
||||
$this->invoice_number_counter += 1;
|
||||
}
|
||||
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function clientNumbersEnabled()
|
||||
{
|
||||
return $this->hasFeature(FEATURE_INVOICE_SETTINGS) && $this->client_number_counter;
|
||||
}
|
||||
}
|
@ -96,6 +96,7 @@ trait PresentsInvoice
|
||||
'client.address1',
|
||||
'client.address2',
|
||||
'client.city_state_postal',
|
||||
'client.postal_city_state',
|
||||
'client.country',
|
||||
'client.email',
|
||||
'client.phone',
|
||||
@ -114,6 +115,7 @@ trait PresentsInvoice
|
||||
'account.address1',
|
||||
'account.address2',
|
||||
'account.city_state_postal',
|
||||
'account.postal_city_state',
|
||||
'account.country',
|
||||
'account.custom_value1',
|
||||
'account.custom_value2',
|
||||
@ -196,6 +198,7 @@ trait PresentsInvoice
|
||||
'id_number',
|
||||
'vat_number',
|
||||
'city_state_postal',
|
||||
'postal_city_state',
|
||||
'country',
|
||||
'email',
|
||||
'contact_name',
|
||||
@ -203,6 +206,11 @@ trait PresentsInvoice
|
||||
'website',
|
||||
'phone',
|
||||
'blank',
|
||||
'adjustment',
|
||||
'tax_invoice',
|
||||
'tax_quote',
|
||||
'statement',
|
||||
'statement_date',
|
||||
];
|
||||
|
||||
foreach ($fields as $field) {
|
||||
|
24
app/Models/Traits/SendsEmails.php
Normal file
24
app/Models/Traits/SendsEmails.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php namespace App\Models\Traits;
|
||||
|
||||
use Utils;
|
||||
use App\Constants\Domain;
|
||||
|
||||
/**
|
||||
* Class SendsEmails
|
||||
*/
|
||||
trait SendsEmails
|
||||
{
|
||||
public function getBccEmail()
|
||||
{
|
||||
return $this->isPro() ? $this->bcc_email : false;
|
||||
}
|
||||
|
||||
public function getFromEmail()
|
||||
{
|
||||
if ( ! $this->isPro() || ! Utils::isNinjaProd() || Utils::isReseller()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Domain::getEmailFromId($this->domain_id);
|
||||
}
|
||||
}
|
@ -7,12 +7,20 @@ use App\Events\UserSettingsChanged;
|
||||
use App\Events\UserSignedUp;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Laracasts\Presenter\PresentableTrait;
|
||||
|
||||
/**
|
||||
* Class User
|
||||
*/
|
||||
class User extends Authenticatable
|
||||
{
|
||||
use PresentableTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $presenter = 'App\Ninja\Presenters\UserPresenter';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@ -130,15 +138,6 @@ class User extends Authenticatable
|
||||
return $this->account->isTrial();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $plan
|
||||
* @return mixed
|
||||
*/
|
||||
public function isEligibleForTrial($plan = null)
|
||||
{
|
||||
return $this->account->isEligibleForTrial($plan);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
|
@ -267,6 +267,14 @@ class Vendor extends EntityModel
|
||||
return 'vendor';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function showMap()
|
||||
{
|
||||
return $this->hasAddress() && env('GOOGLE_MAPS_ENABLED') !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
@ -321,12 +329,14 @@ class Vendor extends EntityModel
|
||||
/**
|
||||
* @return float|int
|
||||
*/
|
||||
public function getTotalExpense()
|
||||
public function getTotalExpenses()
|
||||
{
|
||||
return DB::table('expenses')
|
||||
->where('vendor_id', '=', $this->id)
|
||||
->whereNull('deleted_at')
|
||||
->sum('amount');
|
||||
->select('expense_currency_id', DB::raw('SUM(amount) as amount'))
|
||||
->whereVendorId($this->id)
|
||||
->whereIsDeleted(false)
|
||||
->groupBy('expense_currency_id')
|
||||
->get();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,13 @@ class ActivityDatatable extends EntityDatatable
|
||||
'expense' => $model->expense_public_id ? link_to('/expenses/' . $model->expense_public_id, substr($model->expense_public_notes, 0, 30).'...') : null,
|
||||
];
|
||||
|
||||
return trans("texts.activity_{$model->activity_type_id}", $data);
|
||||
$str = trans("texts.activity_{$model->activity_type_id}", $data);
|
||||
|
||||
if ($model->notes) {
|
||||
$str .= ' - ' . trans("texts.notes_{$model->notes}");
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
],
|
||||
[
|
||||
|
@ -64,4 +64,30 @@ class EntityDatatable
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function rightAlignIndices()
|
||||
{
|
||||
return $this->alignIndices(['amount', 'balance', 'cost']);
|
||||
}
|
||||
|
||||
public function centerAlignIndices()
|
||||
{
|
||||
return $this->alignIndices(['status']);
|
||||
}
|
||||
|
||||
public function alignIndices($fields)
|
||||
{
|
||||
$columns = $this->columnFields();
|
||||
$indices = [];
|
||||
|
||||
foreach ($columns as $index => $column) {
|
||||
if (in_array($column, $fields)) {
|
||||
$indices[] = $index + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return $indices;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
use Utils;
|
||||
use URL;
|
||||
use Auth;
|
||||
use App\Models\Expense;
|
||||
|
||||
class ExpenseDatatable extends EntityDatatable
|
||||
{
|
||||
@ -123,24 +124,10 @@ class ExpenseDatatable extends EntityDatatable
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
private function getStatusLabel($invoiceId, $shouldBeInvoiced, $balance)
|
||||
{
|
||||
if ($invoiceId) {
|
||||
if (floatval($balance)) {
|
||||
$label = trans('texts.invoiced');
|
||||
$class = 'default';
|
||||
} else {
|
||||
$label = trans('texts.paid');
|
||||
$class = 'success';
|
||||
}
|
||||
} elseif ($shouldBeInvoiced) {
|
||||
$label = trans('texts.pending');
|
||||
$class = 'warning';
|
||||
} else {
|
||||
$label = trans('texts.logged');
|
||||
$class = 'primary';
|
||||
}
|
||||
$label = Expense::calcStatusLabel($shouldBeInvoiced, $invoiceId, $balance);
|
||||
$class = Expense::calcStatusClass($shouldBeInvoiced, $invoiceId, $balance);
|
||||
|
||||
return "<h4><div class=\"label label-{$class}\">$label</div></h4>";
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
use Utils;
|
||||
use URL;
|
||||
use Auth;
|
||||
use App\Models\Invoice;
|
||||
|
||||
class InvoiceDatatable extends EntityDatatable
|
||||
{
|
||||
@ -167,35 +168,8 @@ class InvoiceDatatable extends EntityDatatable
|
||||
|
||||
private function getStatusLabel($model)
|
||||
{
|
||||
$entityType = $this->entityType;
|
||||
|
||||
// check if invoice is overdue
|
||||
if (Utils::parseFloat($model->balance) && $model->due_date && $model->due_date != '0000-00-00') {
|
||||
if (\DateTime::createFromFormat('Y-m-d', $model->due_date) < new \DateTime('now')) {
|
||||
$label = $entityType == ENTITY_INVOICE ? trans('texts.overdue') : trans('texts.expired');
|
||||
return '<h4><div class="label label-danger">' . $label . '</div></h4>';
|
||||
}
|
||||
}
|
||||
|
||||
$label = trans('texts.status_' . strtolower($model->invoice_status_name));
|
||||
$class = 'default';
|
||||
switch ($model->invoice_status_id) {
|
||||
case INVOICE_STATUS_SENT:
|
||||
$class = 'info';
|
||||
break;
|
||||
case INVOICE_STATUS_VIEWED:
|
||||
$class = 'warning';
|
||||
break;
|
||||
case INVOICE_STATUS_APPROVED:
|
||||
$class = 'success';
|
||||
break;
|
||||
case INVOICE_STATUS_PARTIAL:
|
||||
$class = 'primary';
|
||||
break;
|
||||
case INVOICE_STATUS_PAID:
|
||||
$class = 'success';
|
||||
break;
|
||||
}
|
||||
$class = Invoice::calcStatusClass($model->invoice_status_id, $model->balance, $model->due_date);
|
||||
$label = Invoice::calcStatusLabel($model->invoice_status_name, $class, $this->entityType, $model->quote_invoice_id);
|
||||
|
||||
return "<h4><div class=\"label label-{$class}\">$label</div></h4>";
|
||||
}
|
||||
@ -206,6 +180,10 @@ class InvoiceDatatable extends EntityDatatable
|
||||
|
||||
if ($this->entityType == ENTITY_INVOICE || $this->entityType == ENTITY_QUOTE) {
|
||||
$actions[] = \DropdownButton::DIVIDER;
|
||||
$actions[] = [
|
||||
'label' => mtrans($this->entityType, 'email_' . $this->entityType),
|
||||
'url' => 'javascript:submitForm_'.$this->entityType.'("emailInvoice")',
|
||||
];
|
||||
$actions[] = [
|
||||
'label' => mtrans($this->entityType, 'mark_sent'),
|
||||
'url' => 'javascript:submitForm_'.$this->entityType.'("markSent")',
|
||||
|
@ -3,6 +3,7 @@
|
||||
use Utils;
|
||||
use URL;
|
||||
use Auth;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentMethod;
|
||||
|
||||
class PaymentDatatable extends EntityDatatable
|
||||
@ -98,7 +99,7 @@ class PaymentDatatable extends EntityDatatable
|
||||
}
|
||||
],
|
||||
[
|
||||
'payment_status_name',
|
||||
'status',
|
||||
function ($model) {
|
||||
return self::getStatusLabel($model);
|
||||
}
|
||||
@ -130,9 +131,7 @@ class PaymentDatatable extends EntityDatatable
|
||||
function ($model) {
|
||||
return Auth::user()->can('editByOwner', [ENTITY_PAYMENT, $model->user_id])
|
||||
&& $model->payment_status_id >= PAYMENT_STATUS_COMPLETED
|
||||
&& $model->refunded < $model->amount
|
||||
&& $model->transaction_reference
|
||||
&& in_array($model->gateway_id , static::$refundableGateways);
|
||||
&& $model->refunded < $model->amount;
|
||||
}
|
||||
]
|
||||
];
|
||||
@ -140,29 +139,10 @@ class PaymentDatatable extends EntityDatatable
|
||||
|
||||
private function getStatusLabel($model)
|
||||
{
|
||||
$label = trans('texts.status_' . strtolower($model->payment_status_name));
|
||||
$class = 'default';
|
||||
switch ($model->payment_status_id) {
|
||||
case PAYMENT_STATUS_PENDING:
|
||||
$class = 'info';
|
||||
break;
|
||||
case PAYMENT_STATUS_COMPLETED:
|
||||
$class = 'success';
|
||||
break;
|
||||
case PAYMENT_STATUS_FAILED:
|
||||
$class = 'danger';
|
||||
break;
|
||||
case PAYMENT_STATUS_PARTIALLY_REFUNDED:
|
||||
$label = trans('texts.status_partially_refunded_amount', [
|
||||
'amount' => Utils::formatMoney($model->refunded, $model->currency_id, $model->country_id),
|
||||
]);
|
||||
$class = 'primary';
|
||||
break;
|
||||
case PAYMENT_STATUS_VOIDED:
|
||||
case PAYMENT_STATUS_REFUNDED:
|
||||
$class = 'default';
|
||||
break;
|
||||
}
|
||||
$amount = Utils::formatMoney($model->refunded, $model->currency_id, $model->country_id);
|
||||
$label = Payment::calcStatusLabel($model->payment_status_id, $model->status, $amount);
|
||||
$class = Payment::calcStatusClass($model->payment_status_id);
|
||||
|
||||
return "<h4><div class=\"label label-{$class}\">$label</div></h4>";
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,12 @@ class RecurringInvoiceDatatable extends EntityDatatable
|
||||
return Utils::fromSqlDate($model->start_date);
|
||||
}
|
||||
],
|
||||
[
|
||||
'last_sent',
|
||||
function ($model) {
|
||||
return Utils::fromSqlDate($model->last_sent_date);
|
||||
}
|
||||
],
|
||||
[
|
||||
'end_date',
|
||||
function ($model) {
|
||||
|
@ -108,21 +108,8 @@ class TaskDatatable extends EntityDatatable
|
||||
|
||||
private function getStatusLabel($model)
|
||||
{
|
||||
if ($model->invoice_number) {
|
||||
if (floatval($model->balance)) {
|
||||
$label = trans('texts.invoiced');
|
||||
$class = 'default';
|
||||
} else {
|
||||
$class = 'success';
|
||||
$label = trans('texts.paid');
|
||||
}
|
||||
} elseif ($model->is_running) {
|
||||
$class = 'primary';
|
||||
$label = trans('texts.running');
|
||||
} else {
|
||||
$class = 'warning';
|
||||
$label = trans('texts.logged');
|
||||
}
|
||||
$label = Task::calcStatusLabel($model->is_running, $model->balance, $model->invoice_number);
|
||||
$class = Task::calcStatusClass($model->is_running, $model->balance, $model->invoice_number);
|
||||
|
||||
return "<h4><div class=\"label label-{$class}\">$label</div></h4>";
|
||||
}
|
||||
|
@ -20,6 +20,12 @@ class TaxRateDatatable extends EntityDatatable
|
||||
function ($model) {
|
||||
return $model->rate . '%';
|
||||
}
|
||||
],
|
||||
[
|
||||
'type',
|
||||
function ($model) {
|
||||
return $model->is_inclusive ? trans('texts.inclusive') : trans('texts.exclusive');
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?php namespace App\Ninja\Import;
|
||||
|
||||
use Carbon;
|
||||
use Utils;
|
||||
use DateTime;
|
||||
use League\Fractal\TransformerAbstract;
|
||||
@ -27,7 +28,7 @@ class BaseTransformer extends TransformerAbstract
|
||||
* @param $name
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasClient($name)
|
||||
public function hasClient($name)
|
||||
{
|
||||
$name = trim(strtolower($name));
|
||||
return isset($this->maps[ENTITY_CLIENT][$name]);
|
||||
@ -37,7 +38,7 @@ class BaseTransformer extends TransformerAbstract
|
||||
* @param $key
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasProduct($key)
|
||||
public function hasProduct($key)
|
||||
{
|
||||
$key = trim(strtolower($key));
|
||||
return isset($this->maps[ENTITY_PRODUCT][$key]);
|
||||
@ -48,7 +49,7 @@ class BaseTransformer extends TransformerAbstract
|
||||
* @param $field
|
||||
* @return string
|
||||
*/
|
||||
protected function getString($data, $field)
|
||||
public function getString($data, $field)
|
||||
{
|
||||
return (isset($data->$field) && $data->$field) ? $data->$field : '';
|
||||
}
|
||||
@ -58,18 +59,28 @@ class BaseTransformer extends TransformerAbstract
|
||||
* @param $field
|
||||
* @return int
|
||||
*/
|
||||
protected function getNumber($data, $field)
|
||||
public function getNumber($data, $field)
|
||||
{
|
||||
return (isset($data->$field) && $data->$field) ? $data->$field : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param $field
|
||||
* @return float
|
||||
*/
|
||||
public function getFloat($data, $field)
|
||||
{
|
||||
return (isset($data->$field) && $data->$field) ? Utils::parseFloat($data->$field) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return null
|
||||
*/
|
||||
protected function getClientId($name)
|
||||
public function getClientId($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
$name = strtolower(trim($name));
|
||||
return isset($this->maps[ENTITY_CLIENT][$name]) ? $this->maps[ENTITY_CLIENT][$name] : null;
|
||||
}
|
||||
|
||||
@ -77,9 +88,9 @@ class BaseTransformer extends TransformerAbstract
|
||||
* @param $name
|
||||
* @return null
|
||||
*/
|
||||
protected function getProductId($name)
|
||||
public function getProductId($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
$name = strtolower(trim($name));
|
||||
return isset($this->maps[ENTITY_PRODUCT][$name]) ? $this->maps[ENTITY_PRODUCT][$name] : null;
|
||||
}
|
||||
|
||||
@ -87,9 +98,9 @@ class BaseTransformer extends TransformerAbstract
|
||||
* @param $name
|
||||
* @return null
|
||||
*/
|
||||
protected function getCountryId($name)
|
||||
public function getCountryId($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
$name = strtolower(trim($name));
|
||||
return isset($this->maps['countries'][$name]) ? $this->maps['countries'][$name] : null;
|
||||
}
|
||||
|
||||
@ -97,9 +108,9 @@ class BaseTransformer extends TransformerAbstract
|
||||
* @param $name
|
||||
* @return null
|
||||
*/
|
||||
protected function getCountryIdBy2($name)
|
||||
public function getCountryIdBy2($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
$name = strtolower(trim($name));
|
||||
return isset($this->maps['countries2'][$name]) ? $this->maps['countries2'][$name] : null;
|
||||
}
|
||||
|
||||
@ -107,7 +118,7 @@ class BaseTransformer extends TransformerAbstract
|
||||
* @param $name
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getFirstName($name)
|
||||
public function getFirstName($name)
|
||||
{
|
||||
$name = Utils::splitName($name);
|
||||
return $name[0];
|
||||
@ -118,10 +129,14 @@ class BaseTransformer extends TransformerAbstract
|
||||
* @param string $format
|
||||
* @return null
|
||||
*/
|
||||
protected function getDate($date, $format = 'Y-m-d')
|
||||
public function getDate($data, $field)
|
||||
{
|
||||
if ( ! $date instanceof DateTime) {
|
||||
$date = DateTime::createFromFormat($format, $date);
|
||||
if ($date = data_get($data, $field)) {
|
||||
try {
|
||||
$date = new Carbon($date);
|
||||
} catch (Exception $e) {
|
||||
// if we fail to parse return blank
|
||||
}
|
||||
}
|
||||
|
||||
return $date ? $date->format('Y-m-d') : null;
|
||||
@ -131,7 +146,7 @@ class BaseTransformer extends TransformerAbstract
|
||||
* @param $name
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getLastName($name)
|
||||
public function getLastName($name)
|
||||
{
|
||||
$name = Utils::splitName($name);
|
||||
return $name[1];
|
||||
@ -141,7 +156,7 @@ class BaseTransformer extends TransformerAbstract
|
||||
* @param $number
|
||||
* @return string
|
||||
*/
|
||||
protected function getInvoiceNumber($number)
|
||||
public function getInvoiceNumber($number)
|
||||
{
|
||||
return str_pad(trim($number), 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
@ -150,7 +165,7 @@ class BaseTransformer extends TransformerAbstract
|
||||
* @param $invoiceNumber
|
||||
* @return null
|
||||
*/
|
||||
protected function getInvoiceId($invoiceNumber)
|
||||
public function getInvoiceId($invoiceNumber)
|
||||
{
|
||||
$invoiceNumber = $this->getInvoiceNumber($invoiceNumber);
|
||||
$invoiceNumber = strtolower($invoiceNumber);
|
||||
@ -161,7 +176,7 @@ class BaseTransformer extends TransformerAbstract
|
||||
* @param $invoiceNumber
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasInvoice($invoiceNumber)
|
||||
public function hasInvoice($invoiceNumber)
|
||||
{
|
||||
$invoiceNumber = $this->getInvoiceNumber($invoiceNumber);
|
||||
$invoiceNumber = strtolower($invoiceNumber);
|
||||
@ -172,7 +187,7 @@ class BaseTransformer extends TransformerAbstract
|
||||
* @param $invoiceNumber
|
||||
* @return null
|
||||
*/
|
||||
protected function getInvoiceClientId($invoiceNumber)
|
||||
public function getInvoiceClientId($invoiceNumber)
|
||||
{
|
||||
$invoiceNumber = $this->getInvoiceNumber($invoiceNumber);
|
||||
$invoiceNumber = strtolower($invoiceNumber);
|
||||
@ -186,7 +201,7 @@ class BaseTransformer extends TransformerAbstract
|
||||
*/
|
||||
public function getVendorId($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
$name = strtolower(trim($name));
|
||||
return isset($this->maps[ENTITY_VENDOR][$name]) ? $this->maps[ENTITY_VENDOR][$name] : null;
|
||||
}
|
||||
|
||||
@ -197,7 +212,7 @@ class BaseTransformer extends TransformerAbstract
|
||||
*/
|
||||
public function getExpenseCategoryId($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
$name = strtolower(trim($name));
|
||||
return isset($this->maps[ENTITY_EXPENSE_CATEGORY][$name]) ? $this->maps[ENTITY_EXPENSE_CATEGORY][$name] : null;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?php namespace App\Ninja\Import\CSV;
|
||||
|
||||
use Utils;
|
||||
use App\Ninja\Import\BaseTransformer;
|
||||
use League\Fractal\Resource\Item;
|
||||
|
||||
@ -16,7 +17,7 @@ class ExpenseTransformer extends BaseTransformer
|
||||
{
|
||||
return new Item($data, function ($data) {
|
||||
return [
|
||||
'amount' => isset($data->amount) ? (float) $data->amount : null,
|
||||
'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,
|
||||
'expense_date' => isset($data->expense_date) ? date('Y-m-d', strtotime($data->expense_date)) : null,
|
||||
|
@ -26,20 +26,21 @@ class InvoiceTransformer extends BaseTransformer
|
||||
return [
|
||||
'client_id' => $this->getClientId($data->name),
|
||||
'invoice_number' => isset($data->invoice_number) ? $this->getInvoiceNumber($data->invoice_number) : null,
|
||||
'paid' => isset($data->paid) ? (float) $data->paid : null,
|
||||
'paid' => $this->getFloat($data, 'paid'),
|
||||
'po_number' => $this->getString($data, 'po_number'),
|
||||
'terms' => $this->getString($data, 'terms'),
|
||||
'public_notes' => $this->getString($data, 'public_notes'),
|
||||
'invoice_date_sql' => isset($data->invoice_date) ? $data->invoice_date : null,
|
||||
'invoice_date_sql' => $this->getDate($data, 'invoice_date'),
|
||||
'due_date_sql' => $this->getDate($data, 'due_date'),
|
||||
'invoice_items' => [
|
||||
[
|
||||
'product_key' => '',
|
||||
'notes' => $this->getString($data, 'notes'),
|
||||
'cost' => isset($data->amount) ? (float) $data->amount : null,
|
||||
'cost' => $this->getFloat($data, 'amount'),
|
||||
'qty' => 1,
|
||||
]
|
||||
],
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,11 +16,11 @@ class PaymentTransformer extends BaseTransformer
|
||||
{
|
||||
return new Item($data, function ($data) {
|
||||
return [
|
||||
'amount' => $data->paid,
|
||||
'amount' => $this->getFloat($data, 'paid'),
|
||||
'payment_date_sql' => isset($data->invoice_date) ? $data->invoice_date : null,
|
||||
'client_id' => $data->client_id,
|
||||
'invoice_id' => $data->invoice_id,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ class ProductTransformer extends BaseTransformer
|
||||
return [
|
||||
'product_key' => $this->getString($data, 'product_key'),
|
||||
'notes' => $this->getString($data, 'notes'),
|
||||
'cost' => $this->getNumber($data, 'cost'),
|
||||
'cost' => $this->getFloat($data, 'cost'),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class InvoiceTransformer extends BaseTransformer
|
||||
'invoice_number' => $this->getInvoiceNumber($data->id),
|
||||
'paid' => (float) $data->paid_amount,
|
||||
'po_number' => $this->getString($data, 'po_number'),
|
||||
'invoice_date_sql' => $this->getDate($data->issue_date, 'm/d/Y'),
|
||||
'invoice_date_sql' => $this->getDate($data, 'issue_date'),
|
||||
'invoice_items' => [
|
||||
[
|
||||
'product_key' => '',
|
||||
@ -40,4 +40,4 @@ class InvoiceTransformer extends BaseTransformer
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,10 +17,10 @@ class PaymentTransformer extends BaseTransformer
|
||||
return new Item($data, function ($data) {
|
||||
return [
|
||||
'amount' => $data->paid_amount,
|
||||
'payment_date_sql' => $this->getDate($data->last_payment_date, 'm/d/Y'),
|
||||
'payment_date_sql' => $this->getDate($data, 'last_payment_date'),
|
||||
'client_id' => $data->client_id,
|
||||
'invoice_id' => $data->invoice_id,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,8 +27,8 @@ class InvoiceTransformer extends BaseTransformer
|
||||
'client_id' => $this->getClientId($data->client),
|
||||
'invoice_number' => $this->getInvoiceNumber($data->statement_no),
|
||||
'paid' => (float) $data->paid_total,
|
||||
'invoice_date_sql' => $this->getDate($data->date),
|
||||
'due_date_sql' => $this->getDate($data->due_date),
|
||||
'invoice_date_sql' => $this->getDate($data, 'date'),
|
||||
'due_date_sql' => $this->getDate($data, 'due_date'),
|
||||
'invoice_items' => [
|
||||
[
|
||||
'product_key' => '',
|
||||
@ -40,4 +40,4 @@ class InvoiceTransformer extends BaseTransformer
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ class PaymentTransformer extends BaseTransformer
|
||||
return new Item($data, function ($data) {
|
||||
return [
|
||||
'amount' => $data->paid_total,
|
||||
'payment_date_sql' => $this->getDate($data->last_paid_on),
|
||||
'payment_date_sql' => $this->getDate($data, 'last_paid_on'),
|
||||
'client_id' => $data->client_id,
|
||||
'invoice_id' => $data->invoice_id,
|
||||
];
|
||||
|
@ -30,8 +30,8 @@ class InvoiceTransformer extends BaseTransformer
|
||||
'po_number' => $this->getString($data, 'purchase_order'),
|
||||
'terms' => $this->getString($data, 'terms'),
|
||||
'public_notes' => $this->getString($data, 'notes'),
|
||||
'invoice_date_sql' => $this->getDate($data->date),
|
||||
'due_date_sql' => $this->getDate($data->due_date),
|
||||
'invoice_date_sql' => $this->getDate($data, 'date'),
|
||||
'due_date_sql' => $this->getDate($data, 'due_date'),
|
||||
'invoice_items' => [
|
||||
[
|
||||
'product_key' => '',
|
||||
@ -43,4 +43,4 @@ class InvoiceTransformer extends BaseTransformer
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ class PaymentTransformer extends BaseTransformer
|
||||
return new Item($data, function ($data) {
|
||||
return [
|
||||
'amount' => (float) $data->paid_to_date,
|
||||
'payment_date_sql' => $this->getDate($data->date),
|
||||
'payment_date_sql' => $this->getDate($data, 'date'),
|
||||
'client_id' => $data->client_id,
|
||||
'invoice_id' => $data->invoice_id,
|
||||
];
|
||||
|
@ -27,8 +27,8 @@ class InvoiceTransformer extends BaseTransformer
|
||||
'client_id' => $this->getClientId($data->customer),
|
||||
'invoice_number' => $this->getInvoiceNumber($data->invoice_num),
|
||||
'po_number' => $this->getString($data, 'po_so'),
|
||||
'invoice_date_sql' => $this->getDate($data->invoice_date),
|
||||
'due_date_sql' => $this->getDate($data->due_date),
|
||||
'invoice_date_sql' => $this->getDate($data, 'invoice_date'),
|
||||
'due_date_sql' => $this->getDate($data, 'due_date'),
|
||||
'paid' => 0,
|
||||
'invoice_items' => [
|
||||
[
|
||||
@ -41,4 +41,4 @@ class InvoiceTransformer extends BaseTransformer
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ class PaymentTransformer extends BaseTransformer
|
||||
return new Item($data, function ($data) {
|
||||
return [
|
||||
'amount' => (float) $data->amount,
|
||||
'payment_date_sql' => $this->getDate($data->payment_date),
|
||||
'payment_date_sql' => $this->getDate($data, 'payment_date'),
|
||||
'client_id' => $this->getInvoiceClientId($data->invoice_num),
|
||||
'invoice_id' => $this->getInvoiceId($data->invoice_num),
|
||||
];
|
||||
|
@ -23,22 +23,45 @@ class InvoiceTransformer extends BaseTransformer
|
||||
}
|
||||
|
||||
return new Item($data, function ($data) {
|
||||
return [
|
||||
$invoice = [
|
||||
'client_id' => $this->getClientId($data->customer_name),
|
||||
'invoice_number' => $this->getInvoiceNumber($data->invoice_number),
|
||||
'paid' => (float) $data->total - (float) $data->balance,
|
||||
'po_number' => $this->getString($data, 'purchaseorder'),
|
||||
'due_date_sql' => $data->due_date,
|
||||
'invoice_date_sql' => $data->invoice_date,
|
||||
'custom_value1' => (float) $data->latefee_amount + (float) $data->adjustment + (float) $data->shipping_charge,
|
||||
'custom_taxes1' => false,
|
||||
'invoice_items' => [
|
||||
[
|
||||
'product_key' => '',
|
||||
'product_key' => $this->getString($data, 'item_name'),
|
||||
'notes' => $this->getString($data, 'item_desc'),
|
||||
'cost' => (float) $data->total,
|
||||
'qty' => 1,
|
||||
'cost' => (float) $data->item_price,
|
||||
'qty' => (float) $data->quantity,
|
||||
'tax_name1' => (float) $data->item_tax1 ? trans('texts.tax') : '',
|
||||
'tax_rate1' => (float) $data->item_tax1,
|
||||
'tax_name2' => (float) $data->item_tax2 ? trans('texts.tax') : '',
|
||||
'tax_rate2' => (float) $data->item_tax2,
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
// we don't support line item discounts so we need to include
|
||||
// the discount as a separate line item
|
||||
if ((float) $data->discount_amount) {
|
||||
$invoice['invoice_items'][] = [
|
||||
'product_key' => '',
|
||||
'notes' => trans('texts.discount'),
|
||||
'cost' => (float) $data->discount_amount * -1,
|
||||
'qty' => 1,
|
||||
'tax_name1' => (float) $data->item_tax1 ? trans('texts.tax') : '',
|
||||
'tax_rate1' => (float) $data->item_tax1,
|
||||
'tax_name2' => (float) $data->item_tax2 ? trans('texts.tax') : '',
|
||||
'tax_rate2' => (float) $data->item_tax2,
|
||||
];
|
||||
}
|
||||
|
||||
return $invoice;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user