Compare commits

...

No commits in common. "gocardless" and "lastFOSS" have entirely different histories.

3949 changed files with 378923 additions and 1235720 deletions

View file

@ -1,3 +0,0 @@
{
"plugins": ["@babel/plugin-proposal-class-properties"]
}

3
.bowerrc Normal file
View file

@ -0,0 +1,3 @@
{
"directory": "./resources/assets/bower"
}

View file

@ -1,5 +0,0 @@
# Codacy configuration file for Invoice Ninja
exclude_paths:
- 'public/css/*'
- 'public/js/*'

37
.codeclimate.yml Normal file
View file

@ -0,0 +1,37 @@
engines:
csslint:
enabled: true
duplication:
enabled: true
config:
languages:
- ruby
- javascript
- python
- php
eslint:
enabled: false
fixme:
enabled: true
phpmd:
enabled: true
ratings:
paths:
- "**.css"
- "**.inc"
- "**.js"
- "**.jsx"
- "**.module"
- "**.php"
- "**.py"
- "**.rb"
exclude_paths:
- "bootstrap/cache"
- "resources/"
- "storage/"
- "tests/"
- "public/"
- "**.md"
- "**.min.js"
- "**.min.php"
- "**.min.css"

View file

@ -1,15 +0,0 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.yml]
indent_size = 2

23
.env.ci
View file

@ -1,23 +0,0 @@
APP_NAME="Invoice Ninja"
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://ninja.test
MULTI_DB_ENABLED=false
# database
DB_CONNECTION=mysql
DB_DATABASE1=ninja
DB_USERNAME1=root
DB_PASSWORD1=ninja
DB_HOST1=127.0.0.1
DB_PORT1=32768
DB_PORT=32768
DB_DATABASE=ninja
DB_USERNAME=root
DB_PASSWORD=ninja
DB_HOST=127.0.0.1
NINJA_ENVIRONMENT=hosted
COMPOSER_AUTH='{"github-oauth": {"github.com": "${{ secrets.GITHUB_TOKEN }}"}}'
TRAVIS=true
API_SECRET=superdoopersecrethere
PHANTOMJS_PDF_GENERATION=false

View file

@ -1,27 +0,0 @@
APP_ENV=local
APP_DEBUG=true
APP_LOCALE=en
APP_URL=http://127.0.0.1:8000
APP_KEY=s7epnjtomsdond5zgfqgaqmwhhcjct02
REQUIRE_HTTPS=false
NINJA_ENVIRONMENT=development
DB_TYPE=mysql
DB_STRICT=false
DB_HOST=localhost
DB_USERNAME=ninja
DB_PASSWORD=ninja
DB_CONNECTION=db-ninja-01
DB_DATABASE1=db-ninja-01
DB_DATABASE2=db-ninja-02
MAIL_MAILER=log
MAIL_PORT=587
MAIL_ENCRYPTION=tls
MAIL_HOST=
MAIL_USERNAME=
MAIL_FROM_NAME=
MAIL_FROM_ADDRESS=
MAIL_PASSWORD=
MAILGUN_DOMAIN=
MAILGUN_SECRET=

View file

@ -1,57 +1,107 @@
APP_NAME="Invoice Ninja"
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=http://www.ninja.test
APP_KEY=SomeRandomStringSomeRandomString
APP_CIPHER=AES-256-CBC
APP_LOCALE=en
APP_URL=http://localhost
DB_CONNECTION=mysql
MULTI_DB_ENABLED=false
DB_TYPE=mysql
DB_STRICT=false
DB_HOST=localhost
DB_DATABASE=ninja
DB_USERNAME=ninja
DB_PASSWORD=ninja
DB_PORT=3306
DEMO_MODE=false
MAIL_DRIVER=smtp
MAIL_PORT=587
MAIL_ENCRYPTION=tls
MAIL_HOST
MAIL_USERNAME
MAIL_FROM_ADDRESS
MAIL_FROM_NAME
MAIL_PASSWORD
BROADCAST_DRIVER=log
LOG_CHANNEL=stack
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
MAILGUN_DOMAIN=
MAILGUN_SECRET=
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
#POSTMARK_API_TOKEN=
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS='user@example.com'
MAIL_FROM_NAME='Self Hosted User'
PHANTOMJS_CLOUD_KEY='a-demo-key-with-low-quota-per-ip-address'
#PHANTOMJS_BIN_PATH=/usr/local/bin/phantomjs
POSTMARK_API_TOKEN=
LOG=single
REQUIRE_HTTPS=false
API_SECRET=password
GOOGLE_MAPS_API_KEY=
ERROR_EMAIL=
TRUSTED_PROXIES=
#TRUSTED_PROXIES=
NINJA_ENVIRONMENT=selfhost
#SESSION_DRIVER=
#SESSION_DOMAIN=
#SESSION_ENCRYPT=
#SESSION_SECURE=
#options - snappdf / phantom / hosted_ninja
PDF_GENERATOR=phantom
#CACHE_DRIVER=
#CACHE_HOST=
#REDIS_HOST=
#CACHE_PORT1=
#CACHE_PORT2=
PHANTOMJS_KEY='a-demo-key-with-low-quota-per-ip-address'
PHANTOMJS_SECRET=secret
#GOOGLE_CLIENT_ID=
#GOOGLE_CLIENT_SECRET=
#GOOGLE_OAUTH_REDIRECT=http://ninja.test/auth/google
UPDATE_SECRET=secret
GOOGLE_MAPS_ENABLED=true
#GOOGLE_MAPS_API_KEY=
COMPOSER_AUTH='{"github-oauth": {"github.com": "${{ secrets.GITHUB_TOKEN }}"}}'
SENTRY_LARAVEL_DSN=https://cc7e8e2c678041689e53e409b7dba236@sentry.invoicing.co/5
# Create a cookie to stay logged in
#REMEMBER_ME_ENABLED=true
# Immediately expire cookie on the browser closing
#SESSION_EXPIRE_ON_CLOSE=false
# The app automatically logs the user out after this number of seconds
#AUTO_LOGOUT_SECONDS=28800
#S3_KEY=
#S3_SECRET=
#S3_REGION=
#S3_BUCKET=
#RACKSPACE_USERNAME=
#RACKSPACE_KEY=
#RACKSPACE_CONTAINER=
#RACKSPACE_REGION=
#RACKSPACE_TEMP_URL_SECRET=
# If this is set to anything, the URL secret will be set the next
# time a file is downloaded through the client portal.
# Only set this temporarily, as it slows things down.
#RACKSPACE_TEMP_URL_SECRET_SET=
#DOCUMENT_FILESYSTEM=
#MAX_DOCUMENT_SIZE # KB
#MAX_EMAIL_DOCUMENTS_SIZE # Total KB
#MAX_ZIP_DOCUMENTS_SIZE # Total KB (uncompressed)
#DOCUMENT_PREVIEW_SIZE # Pixels
WEPAY_CLIENT_ID=
WEPAY_CLIENT_SECRET=
WEPAY_ENVIRONMENT=production # production or stage
WEPAY_AUTO_UPDATE=true # Requires permission from WePay
WEPAY_FEE_PAYER=payee
WEPAY_APP_FEE_CC_MULTIPLIER=0
WEPAY_APP_FEE_ACH_MULTIPLIER=0
WEPAY_APP_FEE_FIXED=0
WEPAY_THEME='{"name":"Invoice Ninja","primary_color":"0b4d78","secondary_color":"0b4d78","background_color":"f8f8f8","button_color":"33b753"}' # See https://www.wepay.com/developer/reference/structures#theme
BLUEVINE_PARTNER_UNIQUE_ID=
BLUEVINE_PARTNER_TOKEN=
CLOUDFLARE_DNS_ENABLED=false
CLOUDFLARE_API_KEY=
CLOUDFLARE_EMAIL=
CLOUDFLARE_TARGET_IP_ADDRESS=
CLOUDFLARE_ZONE_IDS={}

View file

@ -1,24 +1,25 @@
APP_ENV=local
APP_ENV=development
APP_DEBUG=false
APP_URL=http://127.0.0.1:8000
APP_URL=http://ninja.test
APP_KEY=SomeRandomStringSomeRandomString
APP_CIPHER=AES-256-CBC
APP_LOCALE=en
MULTI_DB_ENABLED=true
MULTI_DB_CACHE_ENABLED=true
DB_TYPE=db-ninja-1
DB_STRICT=false
DB_HOST=localhost
DB_USERNAME=ninja
DB_PASSWORD=ninja
DB_CONNECTION=db-ninja-01
DB_DATABASE1=ninja01
DB_DATABASE2=ninja02
DB_DATABASE0=ninja0
DB_DATABASE1=ninja
DB_DATABASE2=ninja2
MAIL_MAILER=log
MAIL_DRIVER=log
TRAVIS=true
API_SECRET=password
TEST_USERNAME=user@example.com
TEST_PERMISSIONS_USERNAME=permissions@example.com
MULTI_DB_ENABLED=true
NINJA_ENVIRONMENT=development

9
.gitattributes vendored
View file

@ -1,5 +1,8 @@
* text=auto
*.css linguist-vendored
*.scss linguist-vendored
*.js linguist-vendored
CHANGELOG.md export-ignore
*.less linguist-vendored
.gitattributes export-ignore
.gitignore export-ignore
.codeclimate.yml export-ignore
.travis.yml export-ignore
.styleci.yml export-ignore

41
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,41 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: triage
assignees: ''
---
**What version of Invoice Ninja are you running? ie v4.5.25 / v5.0.30**
**What environment are you running?**
Docker
Shared Hosting
ZIP
Other
**Have you checked log files (storage/logs/) Please provide redacted output**
**Have you searched existing issues?**
**Have you reported this to Slack/forum before posting?**
**Describe the bug**
A clear and concise description of what the bug is.
**Steps To Reproduce**
Please list the steps to reproduce the issue
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
Note: Before posting don't forget to check our "Troubleshooting" category in the [docs](https://invoiceninja.github.io/docs/self-host-troubleshooting/) (https://invoiceninja.github.io/docs/self-host-troubleshooting/).
**(v5) Can you replicate the issue on our demo site? https://demo.invoiceninja.com**

View file

@ -0,0 +1,24 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: feature request
assignees: ''
---
**What version of Invoice Ninja are you running? ie v4.5 / v5**
**What environment are you running?**
Docker
Shared Hosting
ZIP
Other
**Have you searched existing issues/requests?**
**Screenshots**
If applicable, add screenshots to help explain your request/question.
**Additional context**
Add any other context about the request/question here.

View file

@ -1,112 +0,0 @@
on:
push:
branches:
- v5-develop
pull_request:
branches:
- v5-develop
name: phpunit
jobs:
run:
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
operating-system: ['ubuntu-18.04', 'ubuntu-20.04']
php-versions: ['7.3','7.4','8.0']
phpunit-versions: ['latest']
env:
DB_DATABASE1: ninja
DB_USERNAME1: root
DB_PASSWORD1: ninja
DB_HOST1: '127.0.0.1'
DB_DATABASE: ninja
DB_USERNAME: root
DB_PASSWORD: ninja
DB_HOST: '127.0.0.1'
BROADCAST_DRIVER: log
CACHE_DRIVER: file
QUEUE_CONNECTION: sync
SESSION_DRIVER: file
NINJA_ENVIRONMENT: hosted
MULTI_DB_ENABLED: false
NINJA_LICENSE: 123456
TRAVIS: true
MAIL_MAILER: log
services:
mariadb:
image: mariadb:latest
ports:
- 32768:3306
env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_USER: ninja
MYSQL_PASSWORD: ninja
MYSQL_DATABASE: ninja
MYSQL_ROOT_PASSWORD: ninja
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
steps:
- name: Add hosts to /etc/hosts
run: |
sudo echo "127.0.0.1 ninja.test" | sudo tee -a /etc/hosts
- name: Start mysql service
run: |
sudo /etc/init.d/mysql start
- name: Verify MariaDB connection
env:
DB_PORT: ${{ job.services.mariadb.ports[3306] }}
DB_PORT1: ${{ job.services.mariadb.ports[3306] }}
run: |
while ! mysqladmin ping -h"127.0.0.1" -P"$DB_PORT" --silent; do
sleep 1
done
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mysql, mysqlnd, sqlite3, bcmath, gmp, gd, curl, zip, openssl, mbstring, xml
- uses: actions/checkout@v1
with:
ref: v5-develop
fetch-depth: 1
- name: Copy .env
run: |
cp .env.ci .env
- name: Install composer dependencies
run: |
composer config -g github-oauth.github.com ${{ secrets.GITHUB_TOKEN }}
composer install
- name: Prepare Laravel Application
run: |
php artisan key:generate
php artisan optimize
php artisan cache:clear
php artisan config:cache
- name: Create DB and schemas
run: |
mkdir -p database
touch database/database.sqlite
- name: Migrate Database
run: |
php artisan migrate:fresh --seed --force && php artisan db:seed --force
- name: Prepare JS/CSS assets
run: |
npm i
npm run production
- name: Run Testsuite
run: |
cat .env
vendor/bin/phpunit --testdox
env:
DB_PORT: ${{ job.services.mysql.ports[3306] }}
- name: Run php-cs-fixer
run: |
vendor/bin/php-cs-fixer fix

View file

@ -1,64 +0,0 @@
on:
release:
types: [released]
name: Upload Release Asset
jobs:
build:
name: Upload Release Asset
runs-on: ubuntu-latest
steps:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 7.4
extensions: mysql, mysqlnd, sqlite3, bcmath, gd, curl, zip, openssl, mbstring, xml
- name: Checkout code
uses: actions/checkout@v1
with:
ref: v5-stable
- name: Copy .env file
run: |
cp .env.example .env
- name: Install composer dependencies
run: |
composer config -g github-oauth.github.com ${{ secrets.GITHUB_TOKEN }}
composer install --no-dev
- name: Prepare Laravel Application
run: |
cp .env.example .env
php artisan key:generate
php artisan optimize
php artisan storage:link
sudo php artisan cache:clear
sudo find ./vendor/bin/ -type f -exec chmod +x {} \;
sudo find ./ -type d -exec chmod 755 {} \;
- name: Prepare JS/CSS assets
run: |
npm i
npm run production
- name: Cleanup Builds
run: |
sudo rm -rf bootstrap/cache/*
sudo rm -rf node_modules
sudo rm -rf .git
- name: Build project
run: |
zip -r ./invoiceninja.zip .* -x "../*"
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
files: |
invoiceninja.zip

72
.gitignore vendored
View file

@ -1,32 +1,46 @@
/node_modules
/public/hot
/public/storage
/storage/*.key
/vendor
/.idea
/.vscode
/.vagrant
/tests/_output
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
local_version.txt
.env
.phpunit.result.cache
/resources/assets/bower
/config/staging
/config/development
/config/production
/config/fortrabbit
/config/ubuntu
/config/packages/anahkiasen/rocketeer/
/public/logo
/storage/*
.env.dusk.local
/public/vendors/*
*.log
/public/build
/public/packages
/public/vendor
/resources/assets/bower
/storage/*.key
/storage/documents
/storage/import
/storage/migrations
/bootstrap/compiled.php
/bootstrap/environment.php
/vendor
/node_modules
/.DS_Store
Thumbs.db
/.env
/.env.development.php
/.env.php
/docs/_build
/error_log
/auth.json
/public/error_log
/Modules
# Ignore local migrations
storage/migrations
nbproject
/ninja.sublime-project
/ninja.sublime-workspace
/.phpstorm.meta.php
/_ide_helper.php
/.idea
/.project
tests/_output/
tests/_bootstrap.php
tests/_support/_generated/
.php_cs.cache
public/test.pdf
public/storage/test.pdf
/Modules
# composer stuff
/c3.php
_ide_helper.php
storage/version.txt
storage/framework/.DS_Store

View file

@ -1,10 +1,11 @@
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule "^.env" - [F,L]
# RewriteRule "^storage" - [F,L]
RewriteRule "^storage" - [F,L]
RewriteRule ^(.well-known)($|/) - [L]
RewriteRule ^(.*)$ public/$1 [L]
# https://coderwall.com/p/erbaig/laravel-s-htaccess-to-remove-public-from-url
# RewriteRule ^(.*)$ public/$1 [L]
</IfModule>
# https://github.com/h5bp/server-configs-apache/blob/master/dist/.htaccess

19
.php_cs
View file

@ -1,19 +0,0 @@
<?php
$finder = Symfony\Component\Finder\Finder::create()
->notPath('vendor')
->notPath('bootstrap')
->notPath('storage')
->notPath('node_modules')
->in(__DIR__)
->name('*.php')
->notName('*.blade.php');
return PhpCsFixer\Config::create()
->setRules([
'@PSR2' => true,
'array_syntax' => ['syntax' => 'short'],
'ordered_imports' => ['sortAlgorithm' => 'alpha'],
'no_unused_imports' => true,
])
->setFinder($finder);

98
.php_cs.dist Normal file
View file

@ -0,0 +1,98 @@
<?php
$finder = PhpCsFixer\Finder::create()
->notPath('bootstrap/cache')
->notPath('storage')
->notPath('vendor')
->in(__DIR__)
->name('*.php')
->notName('*.blade.php')
->ignoreDotFiles(true)
->ignoreVCS(true);
return PhpCsFixer\Config::create()
->setRules([
'@PSR2' => true,
'binary_operator_spaces' => true,
'blank_line_after_namespace' => true,
'blank_line_after_opening_tag' => true,
'blank_line_before_return' => true,
'braces' => true,
'cast_spaces' => true,
'class_definition' => true,
'elseif' => true,
'encoding' => true,
'full_opening_tag' => true,
'function_declaration' => true,
'function_typehint_space' => true,
'hash_to_slash_comment' => true,
'heredoc_to_nowdoc' => true,
'include' => true,
'linebreak_after_opening_tag' => true,
'lowercase_cast' => true,
'lowercase_constants' => true,
'lowercase_keywords' => true,
'method_argument_space' => true,
'method_separation' => true,
'native_function_casing' => true,
'new_with_braces' => true,
'no_alias_functions' => true,
'no_blank_lines_after_class_opening' => true,
'no_blank_lines_after_phpdoc' => true,
'no_closing_tag' => true,
'no_empty_phpdoc' => true,
'no_extra_consecutive_blank_lines' => true,
'no_leading_import_slash' => true,
'no_leading_namespace_whitespace' => true,
'no_multiline_whitespace_around_double_arrow' => true,
'no_multiline_whitespace_before_semicolons' => true,
'no_short_bool_cast' => true,
'no_singleline_whitespace_before_semicolons' => true,
'no_spaces_after_function_name' => true,
'no_spaces_inside_parenthesis' => true,
'no_trailing_comma_in_list_call' => true,
'no_trailing_comma_in_singleline_array' => true,
'no_trailing_whitespace' => true,
'no_trailing_whitespace_in_comment' => true,
'no_unneeded_control_parentheses' => true,
'no_unreachable_default_argument_value' => true,
'no_unused_imports' => true,
'no_useless_return' => true,
'no_whitespace_before_comma_in_array' => true,
'not_operator_with_successor_space' => true,
'object_operator_without_whitespace' => true,
'ordered_imports' => true,
'phpdoc_add_missing_param_annotation' => true,
'phpdoc_align' => true,
'phpdoc_indent' => true,
'phpdoc_inline_tag' => true,
'phpdoc_no_access' => true,
'phpdoc_no_package' => true,
'phpdoc_order' => true,
'phpdoc_scalar' => true,
'phpdoc_separation' => true,
'phpdoc_summary' => true,
'phpdoc_to_comment' => true,
'phpdoc_trim' => true,
'phpdoc_types' => true,
'phpdoc_var_without_name' => true,
'self_accessor' => true,
'short_scalar_cast' => true,
'single_blank_line_at_eof' => true,
'single_blank_line_before_namespace' => true,
'single_import_per_statement' => true,
'single_line_after_imports' => true,
'single_quote' => true,
'space_after_semicolon' => true,
'standardize_not_equals' => true,
'switch_case_semicolon_to_colon' => true,
'switch_case_space' => true,
'ternary_operator_spaces' => true,
'trailing_comma_in_multiline_array' => true,
'trim_array_spaces' => true,
'unary_operator_spaces' => true,
'visibility_required' => true,
'whitespace_after_comma_in_array' => true,
'array_syntax' => array('syntax' => 'short'),
])
->setFinder($finder);

View file

@ -1,6 +0,0 @@
{
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"arrowParens": "always"
}

18
.styleci.yml Normal file
View file

@ -0,0 +1,18 @@
preset: laravel
risky: false
enabled:
- no_useless_else
- phpdoc_align
- phpdoc_no_empty_return
- phpdoc_order
- phpdoc_separation
finder:
exclude:
- "resources"
- "storage"
- "tests"
not-path:
- "bootstrap/cache"

138
.travis.yml Normal file
View file

@ -0,0 +1,138 @@
language: php
services:
- mysql
sudo: true
# Prevent tests from taking more than 50 minutes
group: deprecated-2017Q4
php:
- 7.2
- 7.3
addons:
hosts:
- www.ninja.test
cache:
directories:
- vendor
- $HOME/.composer/cache
env:
global:
- COMPOSER_DISCARD_CHANGES=true
- COMPOSER_NO_INTERACTION=1
- COMPOSER_DISABLE_XDEBUG_WARN=1
before_install:
# set GitHub token and update composer
- if [ -n "$GH_TOKEN" ]; then composer config github-oauth.github.com ${GH_TOKEN}; fi;
- composer self-update 1.10.19 && composer -V
# - export USE_ZEND_ALLOC=0
- rvm use 1.9.3 --install --fuzzy
install:
# install Composer dependencies
# - rm composer.lock
# these providers require referencing git commit's which cause Travis to fail
# - sed -i '/mollie/d' composer.json
# - sed -i '/2checkout/d' composer.json
- travis_retry composer install --prefer-dist;
before_script:
# prevent MySQL went away error
- mysql -u root -e 'SET @@GLOBAL.wait_timeout=28800;'
# copy configuration files
- cp .env.travis .env
- cp tests/_bootstrap.php.default tests/_bootstrap.php
- php artisan key:generate --no-interaction
- sed -i '$a NINJA_DEV=true' .env
# create the database and user
- mysql -u root -e "create database IF NOT EXISTS ninja0;"
- mysql -u root -e "create database IF NOT EXISTS ninja;"
- mysql -u root -e "create database IF NOT EXISTS ninja2;"
- mysql -u root -e "GRANT ALL PRIVILEGES ON ninja0.* To 'ninja'@'localhost' IDENTIFIED BY 'ninja'; FLUSH PRIVILEGES;"
- mysql -u root -e "GRANT ALL PRIVILEGES ON ninja.* To 'ninja'@'localhost' IDENTIFIED BY 'ninja'; FLUSH PRIVILEGES;"
- mysql -u root -e "GRANT ALL PRIVILEGES ON ninja2.* To 'ninja'@'localhost' IDENTIFIED BY 'ninja'; FLUSH PRIVILEGES;"
# migrate and seed the database
- php artisan migrate --database=db-ninja-0 --seed --no-interaction
- php artisan migrate --database=db-ninja-1 --seed --no-interaction
- php artisan migrate --database=db-ninja-2 --seed --no-interaction
# Start webserver on ninja.test:8000
- php artisan serve --host=www.ninja.test --port=8000 & # '&' allows to run in background
# Start PhantomJS
- phantomjs --webdriver=4444 & # '&' allows to run in background
# Give it some time to start
- sleep 5
# Make sure the app is up-to-date
- curl -L http://www.ninja.test:8000/update
- php artisan ninja:create-test-data 4 true
- php artisan db:seed --no-interaction --class=UserTableSeeder # development seed
- sed -i 's/DB_TYPE=db-ninja-1/DB_TYPE=db-ninja-2/g' .env
- sed -i 's/user@example.com/user2@example.com/g' .env
- sed -i 's/permissions@example.com/permissions2@example.com/g' .env
- php artisan db:seed --no-interaction --class=UserTableSeeder # development seed
script:
- php ./vendor/codeception/codeception/codecept run --debug acceptance APICest.php
- php ./vendor/codeception/codeception/codecept run --debug acceptance TaxRatesCest.php
- php ./vendor/codeception/codeception/codecept run --debug acceptance CheckBalanceCest.php
- php ./vendor/codeception/codeception/codecept run --debug acceptance ClientCest.php
- php ./vendor/codeception/codeception/codecept run --debug acceptance ExpenseCest.php
- php ./vendor/codeception/codeception/codecept run --debug acceptance CreditCest.php
- php ./vendor/codeception/codeception/codecept run --debug acceptance InvoiceCest.php
- php ./vendor/codeception/codeception/codecept run --debug acceptance QuoteCest.php
- php ./vendor/codeception/codeception/codecept run --debug acceptance InvoiceDesignCest.php
- php ./vendor/codeception/codeception/codecept run --debug acceptance OnlinePaymentCest.php
- php ./vendor/codeception/codeception/codecept run --debug acceptance PaymentCest.php
- php ./vendor/codeception/codeception/codecept run --debug acceptance TaskCest.php
- php ./vendor/codeception/codeception/codecept run --debug acceptance AllPagesCept.php
- php ./vendor/codeception/codeception/codecept run --debug functional PermissionsCest.php
#- sed -i 's/NINJA_DEV=true/NINJA_PROD=true/g' .env
#- php ./vendor/codeception/codeception/codecept run --debug run acceptance GoProCest.php
#- php ./vendor/codeception/codeception/codecept run --debug acceptance GatewayFeesCest.php
#- php ./vendor/codeception/codeception/codecept run --debug acceptance DiscountCest.php
after_script:
- php artisan ninja:check-data --no-interaction --database='db-ninja-1'
- php artisan ninja:check-data --no-interaction --database='db-ninja-2'
- php artisan ninja:init-lookup --validate=true --database='db-ninja-1'
- php artisan ninja:init-lookup --validate=true --database='db-ninja-2'
- cat storage/logs/laravel-error.log
- cat storage/logs/laravel-info.log
- cat .env
- mysql -u root -e 'select * from lookup_companies;' ninja0
- mysql -u root -e 'select * from lookup_accounts;' ninja0
- mysql -u root -e 'select * from lookup_contacts;' ninja0
- mysql -u root -e 'select * from lookup_invitations;' ninja0
- mysql -u root -e 'select * from accounts;' ninja
- mysql -u root -e 'select * from users;' ninja
- mysql -u root -e 'select * from lookup_users;' ninja
- mysql -u root -e 'select * from account_gateways;' ninja
- mysql -u root -e 'select * from clients;' ninja
- mysql -u root -e 'select * from contacts;' ninja
- mysql -u root -e 'select id, public_id, account_id, invoice_number, amount, balance from invoices;' ninja
- mysql -u root -e 'select * from invoice_items;' ninja
- mysql -u root -e 'select * from invitations;' ninja
- mysql -u root -e 'select id, public_id, account_id, invoice_id, amount, transaction_reference from payments;' ninja
- mysql -u root -e 'select * from credits;' ninja
- mysql -u root -e 'select * from expenses;' ninja
- mysql -u root -e 'select * from accounts;' ninja
- mysql -u root -e 'select * from fonts;' ninja
- mysql -u root -e 'select * from banks;' ninja
- mysql -u root -e 'select * from account_gateway_tokens;' ninja
- mysql -u root -e 'select * from payment_methods;' ninja
- FILES=$(find tests/_output -type f -name '*.png' | sort -nr)
- for i in $FILES; do echo $i; base64 "$i"; break; done
notifications:
email:
on_success: never
on_failure: change
slack:
invoiceninja: SLraaKBDvjeRuRtY9o3Yvp1b

47
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,47 @@
# Contributing to Invoice Ninja
Thanks for your contributions!
## Submit bug reports or feature requests
Please discuss the changes with us ahead of time to ensure they will be merged.
### Submit pull requests
* [Fork](https://github.com/invoiceninja/invoiceninja#fork-destination-box) the [Invoice Ninja repository](https://github.com/invoiceninja/invoiceninja)
* Create a new branch with the name `#issue_number-Short-description`
* _Example:_ `#100-Add-GoogleAnalytics`
* Make your changes and commit
* Check if your branch is still in sync with the repositorys **`develop`** branch
* _Read:_ [Syncing a fork](https://help.github.com/articles/syncing-a-fork/)
* _Also read:_ [How to rebase a pull request](https://github.com/edx/edx-platform/wiki/How-to-Rebase-a-Pull-Request)
* Push your branch and create a PR against the Invoice Ninja **`develop`** branch
* Update the [Changelog](CHANGELOG.md)
### Some rules
To make the contribution process nice and easy for anyone, please follow some rules:
* Each contribution(bug or feature) should have an [issue on Github](https://github.com/invoiceninja/invoiceninja/issues)
to give a more detailed explanation.
* Only one feature/bugfix per issue. If you want to submit more, create multiple issues.
* Only one feature/bugfix per PR(pull request). Split more changes into multiple PRs.
#### Coding Style
Try to follow the [PSR-2 guidlines](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
_Example styling:_
```php
/**
* Gets a preview of the email
*
* @param TemplateService $templateService
*
* @return \Illuminate\Http\Response
*/
public function previewEmail(TemplateService $templateService)
{
//
}
```
## Translations
For helping us with translating Invoice Ninja, please use [Transifex](https://www.transifex.com/invoice-ninja/invoice-ninja/).

43
Gruntfile.js Normal file
View file

@ -0,0 +1,43 @@
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
dump_dir: (function() {
var out = {};
grunt.file.expand({ filter: 'isDirectory'}, 'public/fonts/invoice-fonts/*').forEach(function(path) {
var fontName = /[^/]*$/.exec(path)[0],
files = {},
license='';
// Add license text
grunt.file.expand({ filter: 'isFile'}, path+'/*.txt').forEach(function(path) {
var licenseText = grunt.file.read(path);
// Fix anything that could escape from the comment
licenseText = licenseText.replace(/\*\//g,'*\\/');
license += "/*\n"+licenseText+"\n*/";
});
// Create files list
files['public/js/vfs_fonts/'+fontName+'.js'] = [path+'/*.ttf'];
out[fontName] = {
options: {
pre: license+'window.ninjaFontVfs=window.ninjaFontVfs||{};window.ninjaFontVfs.'+fontName+'=',
rootPath: path+'/'
},
files: files
};
});
// Return the computed object
return out;
}())
});
grunt.loadNpmTasks('grunt-dump-dir');
grunt.registerTask('default', ['dump_dir']);
};

88
LICENSE
View file

@ -1,47 +1,47 @@
Elastic License 2.0 (ELv2)
Elastic License
Copyright (c) 2018 by Hillel Coren
Invoice Ninja * https://www.invoiceninja.com
"CREATE. SEND. GET PAID"
Acceptance
By using the software, you agree to all of the terms and conditions below.
All Rights Reserved
ATTRIBUTION ASSURANCE LICENSE (adapted from the original BSD license)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the conditions below are met.
These conditions require a modest attribution to InvoiceNinja.com (the
"Author"), who hopes that its promotional value may help justify the
thousands of dollars in otherwise billable time invested in writing
this and other freely available, open-source software.
Copyright License
The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject to the limitations and conditions below
1. Redistributions of source code, in whole or part and with or without
modification (the "Code"), must prominently display this GPG-signed
text in verifiable form.
2. Redistributions of the Code in binary form must be accompanied by
this GPG-signed text in any documentation and, each time the resulting
executable program or a program dependent thereon is launched, a
prominent display (e.g., splash screen or banner text) of the Author's
attribution information, which includes:
(a) Name ("Hillel Coren"),
(b) Professional identification ("Invoice Ninja"), and
(c) URL ("https://www.invoiceninja.com").
3. Neither the name nor any trademark of the Author may be used to
endorse or promote products derived from this software without specific
prior written permission.
4. Users are entirely responsible, to the exclusion of the Author and
any other persons, for compliance with (1) regulations set by owners or
administrators of employed equipment, (2) licensing terms of any other
software, and (3) local regulations regarding use, including those
regarding import, export, and use of encryption software.
Limitations
You may not provide the software to third parties as a hosted or managed service, where the service provides users with access to any substantial set of the features or functionality of the software.
You may not move, change, disable, or circumvent the license key functionality in the software, and you may not remove or obscure any functionality in the software that is protected by the license key.
You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensors trademarks is subject to applicable law.
Patents
The licensor grants you a license, under any patent claims the licensor can license, or becomes able to license, to make, have made, use, sell, offer for sale, import and have imported the software, in each case subject to the limitations and conditions in this license. This license does not cover any patent claims that you cause to be infringed by modifications or additions to the software. If you or your company make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
Notices
You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms.
If you modify the software, you must include in any modified copies of the software prominent notices stating that you have modified the software.
No Other Rights
These terms do not imply any licenses other than those expressly granted in these terms.
Termination
If you use the software in violation of these terms, such use is not licensed, and your licenses will automatically terminate. If the licensor provides you with a notice of your violation, and you cease all violation of this license no later than 30 days after you receive that notice, your licenses will be reinstated retroactively. However, if you violate these terms after such reinstatement, any additional violation of these terms will cause your licenses to terminate automatically and permanently.
No Liability
As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.
Definitions
The licensor is the entity offering these terms, and the software is the software the licensor makes available under these terms, including any portion of it.
you refers to the individual or entity agreeing to these terms.
your company is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. control means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
your licenses are all the licenses granted to you for the software under these terms.
use means anything you do with the software requiring one of your licenses.
trademark means trademarks, service marks, and similar rights.
For more information regarding the interpretation of this license please see here: https://invoiceninja.github.io/docs/legal/license/
THIS FREE SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR OR ANY CONTRIBUTOR BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
EFFECTS OF UNAUTHORIZED OR MALICIOUS NETWORK ACCESS;
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

210
README.md
View file

@ -1,114 +1,96 @@
<p align="center">
<img src="https://raw.githubusercontent.com/hillelcoren/invoice-ninja/master/public/images/round_logo.png" alt="Sublime's custom image"/>
</p>
![v5-develop phpunit](https://github.com/invoiceninja/invoiceninja/workflows/phpunit/badge.svg?branch=v5-develop)
![v5-stable phpunit](https://github.com/invoiceninja/invoiceninja/workflows/phpunit/badge.svg?branch=v5-stable)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/d16c78aad8574466bf83232b513ef4fb)](https://www.codacy.com/gh/turbo124/invoiceninja/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=turbo124/invoiceninja&amp;utm_campaign=Badge_Grade)
# Invoice Ninja 5
## [Hosted](https://www.invoiceninja.com) | [Self-Hosted](https://www.invoiceninja.org)
### We're on Slack, join us at [slack.invoiceninja.com](http://slack.invoiceninja.com), [forum.invoiceninja.com](https://forum.invoiceninja.com) or if you like [StackOverflow](https://stackoverflow.com/tags/invoice-ninja/)
Just make sure to add the `invoice-ninja` tag to your question.
## Introduction
Version 5 of Invoice Ninja is here! We've taken the best parts of version 4 and bolted on all of the most requested features to produce a invoicing application like no other.
The new interface has a lot more functionality so it isn't a carbon copy of v4, but once you get used to the new layout and functionality we are sure you will love it!
## Referral Program
* Earn 50% of Pro & Enterprise Plans up to 4 years - [Learn more](https://www.invoiceninja.com/referral-program/)
## Recommended Providers
* [Stripe](https://stripe.com/)
* [Postmark](https://postmarkapp.com/)
## Development
* [API Documentation](https://app.swaggerhub.com/apis/invoiceninja/invoiceninja)
* [APP Documentation](https://invoiceninja.github.io/)
## Quick Start
```bash
git clone https://github.com/invoiceninja/invoiceninja.git
git checkout v5-stable
cp .env.example .env
composer update
php artisan key:generate
```
Please Note: Your APP_KEY in the .env file is used to encrypt data, if you lose this you will not be able to run the application.
Run if you want to load sample data, remember to configure .env
```
php artisan migrate:fresh --seed && php artisan db:seed && php artisan ninja:create-test-data
```
To run the web server
```
php artisan serve
```
Navigate to (replace ninja.test as required)
```
http://ninja.test:8000/setup - To setup your configuration if you didn't load sample data.
http://ninja.test:8000/ - For Administrator Logon
user: small@example.com
pass: password
http://ninja.test:8000/client/login - For Client Portal
user: user@example.com
pass: password
```
## Contribution guide.
Code Style to follow [PSR-2](https://www.php-fig.org/psr/psr-2/) standards.
All methods names to be in CamelCase
All variables names to be in snake_case
Where practical code should be strongly typed, ie your methods must return a type ie
`public function doThis() : void`
PHP >= 7.3 allows the return type Nullable so there should be no circumstance a type cannot be return by using the following:
`public function doThat() ?:string`
To improve chances of PRs being merged please include tests to ensure your code works well and integrates with the rest of the project.
## Documentation
API documentation is hosted using Swagger and can be found [HERE](https://app.swaggerhub.com/apis/invoiceninja/invoiceninja)
Installation, Configuration and Troubleshooting documentation can be found [HERE] (https://invoiceninja.github.io)
## Credits
* [Hillel Coren](https://hillelcoren.com/)
* [David Bomba](https://github.com/turbo124)
* [All contributors](https://github.com/invoiceninja/invoiceninja/graphs/contributors)
**Special thanks to:**
* [Holger Lösken](https://github.com/codedge) - [codedge](http://codedge.de)
* [Samuel Laulhau](https://github.com/lalop) - [Lalop](http://lalop.co/)
* [Alexander Vanderveen](https://blog.technicallycomputers.ca/) - [Technically Computers](https://www.technicallycomputers.ca/)
* [Efthymios Sarmpanis](https://github.com/esarbanis)
* [Gianfranco Gasbarri](https://github.com/gincos)
* [Clemens Mol](https://github.com/clemensmol)
* [Benjamin Beganović](https://github.com/beganovich)
## Security
If you find a security issue with this application please send an email to contact@invoiceninja.com Please follow responsible disclosure procedures if you detect an issue. For further information on responsible disclosure please read [here](https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html)
## License
Invoice Ninja is released under the Elastic License.
See [LICENSE](LICENSE) for details.
<p align="center">
<img src="https://raw.githubusercontent.com/hillelcoren/invoice-ninja/master/public/images/round_logo.png" alt="Sublime's custom image"/>
</p>
# Invoice Ninja
[![Build Status](https://travis-ci.org/invoiceninja/invoiceninja.svg?branch=master)](https://travis-ci.org/invoiceninja/invoiceninja)
[![Docs](https://readthedocs.org/projects/invoice-ninja/badge/?version=latest)](https://invoice-ninja.readthedocs.io/en/latest/?badge=latest)
## [Hosted](https://www.invoiceninja.com) | [Self-Hosted](https://www.invoiceninja.org)
### We're on Slack, join us at [slack.invoiceninja.com](http://slack.invoiceninja.com) or if you like [StackOverflow](https://stackoverflow.com/tags/invoice-ninja/)
Just make sure to add the `invoice-ninja` tag to your question.
#### Note: v5 is now tagged Stable! To upgrade from v4 you need to [install v5](https://invoiceninja.github.io/docs/self-host-installation/) as a separate app and then use the migration tool in the latest version of v4 on Settings > Account Management.
All Pro and Enterprise features from the hosted app are included in the open-source code. We offer a $30 per year white-label license to remove the Invoice Ninja branding from client facing parts of the app.
The self-host zip includes all third party libraries whereas downloading the code from GitHub requires using Composer to install the dependencies.
* [Features](https://www.invoiceninja.com/invoicing-features/)
* [Videos](https://www.youtube.com/channel/UCXAHcBvhW05PDtWYIq7WDFA/videos)
* [User Guide](https://docs.invoiceninja.com/)
* [Support Forum](https://www.invoiceninja.com/forums/forum/support/)
* [StackOverflow](https://stackoverflow.com/tags/invoice-ninja/)
## Referral Program
* Earn 50% of Pro & Enterprise Plans up to 4 years - [Learn more](https://www.invoiceninja.com/referral-program/)
## Mobile App
* [iPhone](https://itunes.apple.com/us/app/invoice-ninja/id1435514417?ls=1&mt=8)
* [Android](https://play.google.com/store/apps/details?id=com.invoiceninja.flutter)
* [Source Code](https://github.com/invoiceninja/flutter-mobile)
## Installation Options
* [Ansible](https://github.com/invoiceninja/ansible-installer)
* [Self-Host Zip](https://docs.invoiceninja.com/install.html)
* [Docker File](https://hub.docker.com/r/invoiceninja/invoiceninja/)
* [Cloudron](https://cloudron.io/store/com.invoiceninja.cloudronapp.html)
* [Softaculous](https://www.softaculous.com/apps/ecommerce/Invoice_Ninja)
* [Lando](https://github.com/invoiceninja/invoiceninja/issues/2880)
* [Yunohost](https://github.com/YunoHost-Apps/invoiceninja_ynh)
## Recommended Providers
* [Stripe](https://stripe.com/)
* [Postmark](https://postmarkapp.com/)
## Development
* [API Documentation](https://invoice-ninja.readthedocs.io/en/latest/api.html)
* [PHP SDK](https://github.com/invoiceninja/sdk-php)
* [Zend Framework](https://github.com/alexz707/InvoiceNinjaModule)
* [Custom Module](https://invoice-ninja.readthedocs.io/en/latest/custom_modules.html) | [Watch Video](https://www.youtube.com/watch?v=8jJ-PYuq85k)
## Third Party Modules
* [Event Scheduler](https://github.com/cytech/Scheduler-InvoiceNinja)
* [Manufacturer Module](https://github.com/dicarlosystems/manufacturer-invoiceninja)
* [Point of Sale](https://github.com/dicarlosystems/pointofsale-invoiceninja)
* [Invoice Design Import/Export](https://github.com/feyst/invoicedesignexport)
> Feel free to email us for help if you're working on a module, we're happy to provide developer support.
## Third Party Tools
* [InvoicePlane Import](https://github.com/turbo124/Plane2Ninja)
* [Toggl Sync](https://github.com/Matth--/toggl-invoiceninja-sync)
* [Shopping Cart](https://github.com/Scifabric/invoiceninjashoppingcart)
## Third Party Developers
* [Bold Compass](https://boldcompass.com/customize-invoice-ninja/)
## Contributing
All contributors are welcome!
For information on how contribute to Invoice Ninja, please see our [contributing guide](CONTRIBUTING.md).
## Credits
* [Hillel Coren](https://hillelcoren.com/)
* [David Bomba](https://github.com/turbo124)
* [All contributors](https://github.com/invoiceninja/invoiceninja/graphs/contributors)
**Special thanks to:**
* [Troels Liebe Bentsen](https://github.com/tlbdk)
* [Jeramy Simpson](https://github.com/JeramyMywork) - [MyWork](https://www.mywork.com.au)
* [Sigitas Limontas](https://lt.linkedin.com/in/sigitaslimontas)
* [Joshua Dwire](https://github.com/joshuadwire) - [Bold Compass](https://boldcompass.com/)
* [Holger Lösken](https://github.com/codedge) - [codedge](http://codedge.de)
* [Samuel Laulhau](https://github.com/lalop) - [Lalop](http://lalop.co/)
* [Alexander Zamponi](https://github.com/alexz707)
* [Matthieu Calie](https://github.com/Matth--)
* [Christopher Di Carlo](https://github.com/dicarlosystems) - [Di Carlo Systems Inc.](https://www.dicarlosystems.ca)
* [Kristian Feldsam](https://github.com/feldsam) - [FeldHost™](https://www.feldhost.net)
* [Suhas Sunil Gaikwad](https://github.com/Suhas-Gaikwad)
* [Mike Skaggs](https://github.com/titan-fail)
## License
Invoice Ninja is released under the Attribution Assurance License.
See [LICENSE](LICENSE) for details.

View file

@ -1 +0,0 @@
5.2.18

7
app/Commands/Command.php Normal file
View file

@ -0,0 +1,7 @@
<?php
namespace App\Commands;
abstract class Command
{
}

View file

@ -0,0 +1,126 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\DbServer;
use App\Models\User;
use App\Models\Company;
use App\Libraries\CurlUtils;
class CalculatePayouts extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:calculate-payouts {--type=} {--url=} {--password=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Calculate payouts';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$type = strtolower($this->option('type'));
switch ($type) {
case 'referral':
$this->referralPayouts();
break;
case 'reseller':
$this->resellerPayouts();
break;
}
}
private function referralPayouts()
{
$servers = DbServer::orderBy('id')->get(['name']);
$userMap = [];
foreach ($servers as $server) {
config(['database.default' => $server->name]);
$users = User::where('referral_code', '!=', '')
->get(['email', 'referral_code']);
foreach ($users as $user) {
$userMap[$user->referral_code] = $user->email;
}
}
foreach ($servers as $server) {
config(['database.default' => $server->name]);
$companies = Company::where('referral_code', '!=', '')
->with('payment.client.payments')
->whereNotNull('payment_id')
->get();
$this->info('User,Client,Date,Amount,Reference');
foreach ($companies as $company) {
if (!isset($userMap[$company->referral_code])) {
continue;
}
$user = $userMap[$company->referral_code];
$payment = $company->payment;
if ($payment) {
$client = $payment->client;
foreach ($client->payments as $payment) {
$amount = $payment->getCompletedAmount();
$this->info('"' . $user . '",' .
'"' . $client->getDisplayName() . '",' .
$payment->payment_date . ',' .
$amount . ',' .
$payment->transaction_reference
);
}
}
}
}
}
private function resellerPayouts()
{
$response = CurlUtils::post($this->option('url') . '/reseller_stats', [
'password' => $this->option('password')
]);
$this->info('Response:');
$this->info($response);
}
protected function getOptions()
{
return [
['type', null, InputOption::VALUE_OPTIONAL, 'Type', null],
['url', null, InputOption::VALUE_OPTIONAL, 'Url', null],
['password', null, InputOption::VALUE_OPTIONAL, 'Password', null],
];
}
}

View file

@ -0,0 +1,132 @@
<?php
namespace App\Console\Commands;
use App\Models\Account;
use App\Models\Invoice;
use App\Ninja\Mailers\ContactMailer as Mailer;
use App\Ninja\Repositories\AccountRepository;
use App\Services\PaymentService;
use Illuminate\Console\Command;
use Carbon;
use Symfony\Component\Console\Input\InputOption;
/**
* Class ChargeRenewalInvoices.
*/
class ChargeRenewalInvoices extends Command
{
/**
* @var string
*/
protected $name = 'ninja:charge-renewals';
/**
* @var string
*/
protected $description = 'Charge renewal invoices';
/**
* @var Mailer
*/
protected $mailer;
/**
* @var AccountRepository
*/
protected $accountRepo;
/**
* @var PaymentService
*/
protected $paymentService;
/**
* ChargeRenewalInvoices constructor.
*
* @param Mailer $mailer
* @param AccountRepository $repo
* @param PaymentService $paymentService
*/
public function __construct(Mailer $mailer, AccountRepository $repo, PaymentService $paymentService)
{
parent::__construct();
$this->mailer = $mailer;
$this->accountRepo = $repo;
$this->paymentService = $paymentService;
}
public function handle()
{
$this->info(date('r').' ChargeRenewalInvoices...');
if ($database = $this->option('database')) {
config(['database.default' => $database]);
}
$ninjaAccount = $this->accountRepo->getNinjaAccount();
$invoices = Invoice::whereAccountId($ninjaAccount->id)
->whereDueDate(date('Y-m-d'))
->where('balance', '>', 0)
->with('client')
->orderBy('id')
->get();
$this->info($invoices->count() . ' invoices found');
foreach ($invoices as $invoice) {
// check if account has switched to free since the invoice was created
$account = Account::find($invoice->client->public_id);
if (! $account) {
continue;
}
$company = $account->company;
if (! $company->plan || $company->plan == PLAN_FREE) {
continue;
}
if (Carbon::parse($company->plan_expires)->isFuture()) {
$this->info('Skipping invoice ' . $invoice->invoice_number . ' [plan not expired]');
continue;
}
$this->info("Charging invoice {$invoice->invoice_number}");
if (! $this->paymentService->autoBillInvoice($invoice)) {
$this->info('Failed to auto-bill, emailing invoice');
$this->mailer->sendInvoice($invoice);
}
}
$this->info('Done');
if ($errorEmail = env('ERROR_EMAIL')) {
\Mail::raw('EOM', function ($message) use ($errorEmail) {
$message->to($errorEmail)
->from(CONTACT_EMAIL)
->subject('ChargeRenewalInvoices: Finished successfully');
});
}
}
/**
* @return array
*/
protected function getArguments()
{
return [];
}
/**
* @return array
*/
protected function getOptions()
{
return [
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
];
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,165 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Console\Commands;
use App;
use App\Factory\ClientContactFactory;
use App\Models\Account;
use App\Models\Activity;
use App\Models\Backup;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\ClientGatewayToken;
use App\Models\Company;
use App\Models\CompanyGateway;
use App\Models\CompanyLedger;
use App\Models\CompanyToken;
use App\Models\CompanyUser;
use App\Models\Contact;
use App\Models\Credit;
use App\Models\CreditInvitation;
use App\Models\Design;
use App\Models\Document;
use App\Models\Expense;
use App\Models\ExpenseCategory;
use App\Models\Gateway;
use App\Models\GroupSetting;
use App\Models\Invoice;
use App\Models\InvoiceInvitation;
use App\Models\Payment;
use App\Models\PaymentHash;
use App\Models\Paymentable;
use App\Models\Product;
use App\Models\Project;
use App\Models\Quote;
use App\Models\QuoteInvitation;
use App\Models\RecurringInvoice;
use App\Models\RecurringInvoiceInvitation;
use App\Models\Subscription;
use App\Models\SystemLog;
use App\Models\Task;
use App\Models\TaskStatus;
use App\Models\TaxRate;
use App\Models\User;
use App\Models\Vendor;
use App\Models\VendorContact;
use App\Models\Webhook;
use App\Utils\Ninja;
use DB;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
use Mail;
use Symfony\Component\Console\Input\InputOption;
/**
* Class CheckDb.
*/
class CheckDb extends Command
{
/**
* @var string
*/
protected $signature = 'ninja:check-db';
/**
* @var string
*/
protected $description = 'Check MultiDB';
protected $log = '';
private $entities = [
Account::class,
Activity::class,
Backup::class,
Client::class,
ClientContact::class,
ClientGatewayToken::class,
Company::class,
CompanyGateway::class,
CompanyLedger::class,
CompanyToken::class,
CompanyUser::class,
Credit::class,
CreditInvitation::class,
Design::class,
Document::class,
Expense::class,
ExpenseCategory::class,
Gateway::class,
GroupSetting::class,
Invoice::class,
InvoiceInvitation::class,
Payment::class,
Paymentable::class,
PaymentHash::class,
Product::class,
Project::class,
Quote::class,
QuoteInvitation::class,
RecurringInvoice::class,
RecurringInvoiceInvitation::class,
Subscription::class,
SystemLog::class,
Task::class,
TaskStatus::class,
TaxRate::class,
User::class,
Vendor::class,
VendorContact::class,
WebHook::class,
];
public function handle()
{
$this->LogMessage("Checking - V5_DB1");
foreach($this->entities as $entity) {
$count_db_1 = $entity::on('db-ninja-01')->count();
$count_db_2 = $entity::on('db-ninja-02a')->count();
$diff = $count_db_1 - $count_db_2;
if($diff != 0)
$this->logMessage("{$entity} DB1: {$count_db_1} - DB2: {$count_db_2} - diff = {$diff}");
}
$this->LogMessage("Checking - V5_DB2");
foreach($this->entities as $entity) {
$count_db_1 = $entity::on('db-ninja-02')->count();
$count_db_2 = $entity::on('db-ninja-01a')->count();
$diff = $count_db_1 - $count_db_2;
if($diff != 0)
$this->logMessage("{$entity} DB1: {$count_db_1} - DB2: {$count_db_2} - diff = {$diff}");
}
}
private function logMessage($str)
{
$str = date('Y-m-d h:i:s').' '.$str;
$this->info($str);
$this->log .= $str."\n";
}
}

View file

@ -1,168 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Console\Commands;
use App\DataMapper\CompanySettings;
use App\DataMapper\FeesAndLimits;
use App\Events\Invoice\InvoiceWasCreated;
use App\Factory\InvoiceFactory;
use App\Factory\InvoiceItemFactory;
use App\Helpers\Invoice\InvoiceSum;
use App\Jobs\Company\CreateCompanyPaymentTerms;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Jobs\Util\VersionCheck;
use App\Models\Account;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Company;
use App\Models\CompanyGateway;
use App\Models\CompanyToken;
use App\Models\Country;
use App\Models\Credit;
use App\Models\Expense;
use App\Models\Product;
use App\Models\Project;
use App\Models\Quote;
use App\Models\Task;
use App\Models\User;
use App\Models\Vendor;
use App\Models\VendorContact;
use App\Repositories\InvoiceRepository;
use App\Utils\Ninja;
use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesHash;
use Carbon\Carbon;
use Faker\Factory;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
class CreateAccount extends Command
{
use MakesHash, GeneratesCounter;
/**
* @var string
*/
protected $description = 'Create Single Account';
/**
* @var string
*/
protected $signature = 'ninja:create-account {--email=} {--password=}';
/**
* Create a new command instance.
*
* @param InvoiceRepository $invoice_repo
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->info(date('r').' Create Single Account...');
$this->warmCache();
$this->createAccount();
}
private function createAccount()
{
$account = Account::factory()->create();
$company = Company::factory()->create([
'account_id' => $account->id,
'portal_domain' => config('ninja.app_url'),
'portal_mode' => 'domain',
]);
$account->default_company_id = $company->id;
$account->save();
$email = $this->option('email') ?? 'admin@example.com';
$password = $this->option('password') ?? 'changeme!';
$user = User::factory()->create([
'account_id' => $account->id,
'email' => $email,
'password' => Hash::make($password),
'confirmation_code' => $this->createDbHash(config('database.default')),
'email_verified_at' => now(),
'first_name' => 'New',
'last_name' => 'User',
'phone' => '',
]);
$company_token = new CompanyToken;
$company_token->user_id = $user->id;
$company_token->company_id = $company->id;
$company_token->account_id = $account->id;
$company_token->name = 'User Token';
$company_token->token = Str::random(64);
$company_token->is_system = true;
$company_token->save();
$user->companies()->attach($company->id, [
'account_id' => $account->id,
'is_owner' => 1,
'is_admin' => 1,
'is_locked' => 0,
'notifications' => CompanySettings::notificationDefaults(),
'settings' => null,
]);
CreateCompanyPaymentTerms::dispatchNow($company, $user);
CreateCompanyTaskStatuses::dispatchNow($company, $user);
VersionCheck::dispatchNow();
}
private function warmCache()
{
/* Warm up the cache !*/
$cached_tables = config('ninja.cached_tables');
foreach ($cached_tables as $name => $class) {
if (! Cache::has($name)) {
// check that the table exists in case the migration is pending
if (! Schema::hasTable((new $class())->getTable())) {
continue;
}
if ($name == 'payment_terms') {
$orderBy = 'num_days';
} elseif ($name == 'fonts') {
$orderBy = 'sort_order';
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
$orderBy = 'name';
} else {
$orderBy = 'id';
}
$tableData = $class::orderBy($orderBy)->get();
if ($tableData->count()) {
Cache::forever($name, $tableData);
}
}
}
}
}

View file

@ -0,0 +1,223 @@
<?php
namespace App\Console\Commands;
use Utils;
use stdClass;
use App\Models\Account;
use Faker\Factory;
use Illuminate\Console\Command;
/**
* Class CreateLuisData.
*/
class CreateLuisData extends Command
{
/**
* @var string
*/
protected $description = 'Create LUIS Data';
/**
* @var string
*/
protected $signature = 'ninja:create-luis-data {faker_field=name}';
/**
* CreateLuisData constructor.
*
*/
public function __construct()
{
parent::__construct();
$this->faker = Factory::create();
}
/**
* @return bool
*/
public function handle()
{
$this->fakerField = $this->argument('faker_field');
$intents = [];
$entityTypes = [
ENTITY_INVOICE,
ENTITY_QUOTE,
ENTITY_CLIENT,
ENTITY_CREDIT,
ENTITY_EXPENSE,
ENTITY_PAYMENT,
ENTITY_PRODUCT,
ENTITY_RECURRING_INVOICE,
ENTITY_TASK,
ENTITY_VENDOR,
];
foreach ($entityTypes as $entityType) {
$intents = array_merge($intents, $this->createIntents($entityType));
}
$intents = array_merge($intents, $this->getNavigateToIntents($entityType));
$this->info(json_encode($intents));
}
private function createIntents($entityType)
{
$intents = [];
$intents = array_merge($intents, $this->getCreateEntityIntents($entityType));
$intents = array_merge($intents, $this->getFindEntityIntents($entityType));
$intents = array_merge($intents, $this->getListEntityIntents($entityType));
return $intents;
}
private function getCreateEntityIntents($entityType)
{
$intents = [];
$phrases = [
"create new {$entityType}",
"new {$entityType}",
"make a {$entityType}",
];
foreach ($phrases as $phrase) {
$intents[] = $this->createIntent('CreateEntity', $phrase, [
$entityType => 'EntityType',
]);
if ($entityType != ENTITY_CLIENT) {
$client = $this->faker->{$this->fakerField};
$phrase .= " for {$client}";
$intents[] = $this->createIntent('CreateEntity', $phrase, [
$entityType => 'EntityType',
$client => 'Name',
]);
}
}
return $intents;
}
private function getFindEntityIntents($entityType)
{
$intents = [];
if (in_array($entityType, [ENTITY_CLIENT, ENTITY_INVOICE, ENTITY_QUOTE])) {
$name = $entityType === ENTITY_CLIENT ? $this->faker->{$this->fakerField} : $this->faker->randomNumber(4);
$intents[] = $this->createIntent('FindEntity', "find {$entityType} {$name}", [
$entityType => 'EntityType',
$name => 'Name',
]);
if ($entityType === ENTITY_CLIENT) {
$name = $this->faker->{$this->fakerField};
$intents[] = $this->createIntent('FindEntity', "find {$name}", [
$name => 'Name',
]);
}
}
return $intents;
}
private function getListEntityIntents($entityType)
{
$intents = [];
$entityTypePlural = Utils::pluralizeEntityType($entityType);
$intents[] = $this->createIntent('ListEntity', "show me {$entityTypePlural}", [
$entityTypePlural => 'EntityType',
]);
$intents[] = $this->createIntent('ListEntity', "list {$entityTypePlural}", [
$entityTypePlural => 'EntityType',
]);
$intents[] = $this->createIntent('ListEntity', "show me active {$entityTypePlural}", [
$entityTypePlural => 'EntityType',
'active' => 'Filter',
]);
$intents[] = $this->createIntent('ListEntity', "list archived and deleted {$entityTypePlural}", [
$entityTypePlural => 'EntityType',
'archived' => 'Filter',
'deleted' => 'Filter',
]);
if ($entityType != ENTITY_CLIENT) {
$client = $this->faker->{$this->fakerField};
$intents[] = $this->createIntent('ListEntity', "list {$entityTypePlural} for {$client}", [
$entityTypePlural => 'EntityType',
$client => 'Name',
]);
$intents[] = $this->createIntent('ListEntity', "show me {$client}'s {$entityTypePlural}", [
$entityTypePlural => 'EntityType',
$client . '\'s' => 'Name',
]);
$intents[] = $this->createIntent('ListEntity', "show me {$client}'s active {$entityTypePlural}", [
$entityTypePlural => 'EntityType',
$client . '\'s' => 'Name',
'active' => 'Filter',
]);
}
return $intents;
}
private function getNavigateToIntents($entityType)
{
$intents = [];
$locations = array_merge(Account::$basicSettings, Account::$advancedSettings);
foreach ($locations as $location) {
$location = str_replace('_', ' ', $location);
$intents[] = $this->createIntent('NavigateTo', "go to {$location}", [
$location => 'Location',
]);
$intents[] = $this->createIntent('NavigateTo', "show me {$location}", [
$location => 'Location',
]);
}
return $intents;
}
private function createIntent($name, $text, $entities)
{
$intent = new stdClass();
$intent->intent = $name;
$intent->text = $text;
$intent->entities = [];
foreach ($entities as $value => $entity) {
$startPos = strpos($text, (string)$value);
if (! $startPos) {
dd("Failed to find {$value} in {$text}");
}
$entityClass = new stdClass();
$entityClass->entity = $entity;
$entityClass->startPos = $startPos;
$entityClass->endPos = $entityClass->startPos + strlen($value) - 1;
$intent->entities[] = $entityClass;
}
return $intent;
}
/**
* @return array
*/
protected function getArguments()
{
return [];
}
/**
* @return array
*/
protected function getOptions()
{
return [];
}
}

View file

@ -1,834 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Console\Commands;
use App\DataMapper\CompanySettings;
use App\DataMapper\FeesAndLimits;
use App\Events\Invoice\InvoiceWasCreated;
use App\Events\RecurringInvoice\RecurringInvoiceWasCreated;
use App\Factory\GroupSettingFactory;
use App\Factory\InvoiceFactory;
use App\Factory\InvoiceItemFactory;
use App\Factory\RecurringInvoiceFactory;
use App\Factory\SubscriptionFactory;
use App\Helpers\Invoice\InvoiceSum;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Models\Account;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Company;
use App\Models\CompanyGateway;
use App\Models\CompanyToken;
use App\Models\Country;
use App\Models\Credit;
use App\Models\Expense;
use App\Models\Product;
use App\Models\Project;
use App\Models\Quote;
use App\Models\RecurringInvoice;
use App\Models\Task;
use App\Models\TaxRate;
use App\Models\User;
use App\Models\Vendor;
use App\Models\VendorContact;
use App\Repositories\InvoiceRepository;
use App\Utils\Ninja;
use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesHash;
use Carbon\Carbon;
use Faker\Factory;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use stdClass;
class CreateSingleAccount extends Command
{
use MakesHash, GeneratesCounter;
/**
* @var string
*/
protected $description = 'Create Single Sample Account';
/**
* @var string
*/
protected $signature = 'ninja:create-single-account {gateway=all}';
protected $invoice_repo;
protected $count;
protected $gateway;
/**
* Create a new command instance.
*
* @param InvoiceRepository $invoice_repo
*/
public function __construct(InvoiceRepository $invoice_repo)
{
parent::__construct();
$this->invoice_repo = $invoice_repo;
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->info(date('r').' Create Single Sample Account...');
$this->count = 1;
$this->gateway = $this->argument('gateway');
$this->info('Warming up cache');
$this->warmCache();
$this->createSmallAccount();
}
private function createSmallAccount()
{
$this->info('Creating Small Account and Company');
$account = Account::factory()->create();
$company = Company::factory()->create([
'account_id' => $account->id,
'slack_webhook_url' => config('ninja.notification.slack'),
'default_password_timeout' => 30*60000,
'portal_mode' => 'domain',
'portal_domain' => 'http://ninja.test:8000',
]);
$settings = $company->settings;
$settings->invoice_terms = 'Default company invoice terms';
$settings->quote_terms = 'Default company quote terms';
$settings->invoice_footer = 'Default invoice footer';
$company->settings = $settings;
$company->save();
$account->default_company_id = $company->id;
$account->save();
$user = User::whereEmail('small@example.com')->first();
if (! $user) {
$user = User::factory()->create([
'account_id' => $account->id,
'email' => 'small@example.com',
'confirmation_code' => $this->createDbHash(config('database.default')),
]);
}
$company_token = new CompanyToken;
$company_token->user_id = $user->id;
$company_token->company_id = $company->id;
$company_token->account_id = $account->id;
$company_token->name = 'test token';
$company_token->token = 'company-token-test';
$company_token->is_system = true;
$company_token->save();
$user->companies()->attach($company->id, [
'account_id' => $account->id,
'is_owner' => 1,
'is_admin' => 1,
'is_locked' => 0,
'notifications' => CompanySettings::notificationDefaults(),
'settings' => null,
]);
Product::factory()->count(1)->create([
'user_id' => $user->id,
'company_id' => $company->id,
]);
TaxRate::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'name' => 'GST',
'rate' => 10
]);
TaxRate::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'name' => 'VAT',
'rate' => 17.5
]);
TaxRate::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'name' => 'CA Sales Tax',
'rate' => 5
]);
$this->info('Creating '.$this->count.' clients');
for ($x = 0; $x < $this->count; $x++) {
$z = $x + 1;
$this->info('Creating client # '.$z);
$this->createClient($company, $user);
}
CreateCompanyTaskStatuses::dispatchNow($company, $user);
for ($x = 0; $x < $this->count; $x++) {
$client = $company->clients->random();
$this->info('creating invoice for client #'.$client->id);
$this->createInvoice($client);
$this->info('creating invoice for client #'.$client->id);
$this->createInvoice($client);
$this->info('creating invoice for client #'.$client->id);
$this->createInvoice($client);
$this->info('creating invoice for client #'.$client->id);
$this->createInvoice($client);
$this->info('creating invoice for client #'.$client->id);
$this->createInvoice($client);
$client = $company->clients->random();
// $this->info('creating credit for client #'.$client->id);
// $this->createCredit($client); /** Prevents Stripe from running payments. */
$client = $company->clients->random();
$this->info('creating quote for client #'.$client->id);
$this->createQuote($client);
$client = $company->clients->random();
$this->info('creating expense for client #'.$client->id);
$this->createExpense($client);
$client = $company->clients->random();
$this->info('creating vendor for client #'.$client->id);
$this->createVendor($client);
$client = $company->clients->random();
$this->info('creating task for client #' . $client->id);
$this->createTask($client);
$client = $company->clients->random();
$this->info('creating project for client #' . $client->id);
$this->createProject($client);
$this->info('creating credit for client #' . $client->id);
$this->createCredit($client);
$this->info('creating recurring invoice for client # ' . $client->id);
$this->createRecurringInvoice($client);
}
$this->createGateways($company, $user);
$this->createSubsData($company, $user);
}
private function createSubsData($company, $user)
{
$gs = GroupSettingFactory::create($company->id, $user->id);
$gs->name = "plans";
$gs->save();
$p1 = Product::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'product_key' => 'pro_plan',
'notes' => 'The Pro Plan',
'cost' => 10,
'price' => 10,
'quantity' => 1,
]);
$p2 = Product::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'product_key' => 'enterprise_plan',
'notes' => 'The Enterprise Plan',
'cost' => 14,
'price' => 14,
'quantity' => 1,
]);
$p3 = Product::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
'product_key' => 'free_plan',
'notes' => 'The Free Plan',
'cost' => 0,
'price' => 0,
'quantity' => 1,
]);
$webhook_config = [
'post_purchase_url' => 'http://ninja.test:8000/api/admin/plan',
'post_purchase_rest_method' => 'POST',
'post_purchase_headers' => [],
];
$sub = SubscriptionFactory::create($company->id, $user->id);
$sub->name = "Pro Plan";
$sub->group_id = $gs->id;
$sub->recurring_product_ids = "{$p1->hashed_id}";
$sub->webhook_configuration = $webhook_config;
$sub->allow_plan_changes = true;
$sub->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
$sub->save();
$sub = SubscriptionFactory::create($company->id, $user->id);
$sub->name = "Enterprise Plan";
$sub->group_id = $gs->id;
$sub->recurring_product_ids = "{$p2->hashed_id}";
$sub->webhook_configuration = $webhook_config;
$sub->allow_plan_changes = true;
$sub->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
$sub->save();
$sub = SubscriptionFactory::create($company->id, $user->id);
$sub->name = "Free Plan";
$sub->group_id = $gs->id;
$sub->recurring_product_ids = "{$p3->hashed_id}";
$sub->webhook_configuration = $webhook_config;
$sub->allow_plan_changes = true;
$sub->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
$sub->save();
}
private function createClient($company, $user)
{
// dispatch(function () use ($company, $user) {
// });
$client = Client::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
]);
ClientContact::factory()->create([
'user_id' => $user->id,
'client_id' => $client->id,
'company_id' => $company->id,
'is_primary' => 1,
'email' => 'user@example.com'
]);
ClientContact::factory()->count(rand(1, 2))->create([
'user_id' => $user->id,
'client_id' => $client->id,
'company_id' => $company->id,
]);
$client->number = $this->getNextClientNumber($client);
$settings = $client->settings;
$settings->currency_id = "1";
// $settings->use_credits_payment = "always";
$client->settings = $settings;
$country = Country::all()->random();
$client->country_id = $country->id;
$client->save();
}
private function createExpense($client)
{
Expense::factory()->count(rand(1, 2))->create([
'user_id' => $client->user->id,
'client_id' => $client->id,
'company_id' => $client->company->id,
]);
}
private function createVendor($client)
{
$vendor = Vendor::factory()->create([
'user_id' => $client->user->id,
'company_id' => $client->company->id,
]);
VendorContact::factory()->create([
'user_id' => $client->user->id,
'vendor_id' => $vendor->id,
'company_id' => $client->company->id,
'is_primary' => 1,
]);
VendorContact::factory()->count(rand(1, 2))->create([
'user_id' => $client->user->id,
'vendor_id' => $vendor->id,
'company_id' => $client->company->id,
'is_primary' => 0,
]);
}
private function createTask($client)
{
$vendor = Task::factory()->create([
'user_id' => $client->user->id,
'company_id' => $client->company->id,
]);
}
private function createProject($client)
{
$vendor = Project::factory()->create([
'user_id' => $client->user->id,
'company_id' => $client->company->id,
]);
}
private function createInvoice($client)
{
$faker = Factory::create();
$invoice = InvoiceFactory::create($client->company->id, $client->user->id); //stub the company and user_id
$invoice->client_id = $client->id;
$dateable = Carbon::now()->subDays(rand(0, 90));
$invoice->date = $dateable;
$invoice->line_items = $this->buildLineItems(rand(1, 10));
$invoice->uses_inclusive_taxes = false;
if (rand(0, 1)) {
$invoice->tax_name1 = 'GST';
$invoice->tax_rate1 = 10.00;
}
if (rand(0, 1)) {
$invoice->tax_name2 = 'VAT';
$invoice->tax_rate2 = 17.50;
}
if (rand(0, 1)) {
$invoice->tax_name3 = 'CA Sales Tax';
$invoice->tax_rate3 = 5;
}
$invoice->custom_value1 = $faker->date;
$invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no';
$invoice->save();
$invoice_calc = new InvoiceSum($invoice);
$invoice_calc->build();
$invoice = $invoice_calc->getInvoice();
if ($this->gateway === 'braintree') {
$invoice->amount = 100; // Braintree sandbox only allows payments under 2,000 to complete successfully.
}
$invoice->save();
$invoice->service()->createInvitations()->markSent();
$this->invoice_repo->markSent($invoice);
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars()));
}
private function createCredit($client)
{
$faker = Factory::create();
$credit = Credit::factory()->create(['user_id' => $client->user->id, 'company_id' => $client->company->id, 'client_id' => $client->id]);
$dateable = Carbon::now()->subDays(rand(0, 90));
$credit->date = $dateable;
$credit->line_items = $this->buildCreditItem();
$credit->uses_inclusive_taxes = false;
$credit->save();
$invoice_calc = new InvoiceSum($credit);
$invoice_calc->build();
$credit = $invoice_calc->getCredit();
$credit->save();
$credit->service()->markSent()->save();
$credit->service()->createInvitations();
}
private function createQuote($client)
{
$faker = Factory::create();
$quote = Quote::factory()->create(['user_id' => $client->user->id, 'company_id' => $client->company->id, 'client_id' => $client->id]);
$quote->date = $faker->date();
$quote->client_id = $client->id;
$quote->setRelation('client', $client);
$quote->line_items = $this->buildLineItems(rand(1, 10));
$quote->uses_inclusive_taxes = false;
if (rand(0, 1)) {
$quote->tax_name1 = 'GST';
$quote->tax_rate1 = 10.00;
}
if (rand(0, 1)) {
$quote->tax_name2 = 'VAT';
$quote->tax_rate2 = 17.50;
}
if (rand(0, 1)) {
$quote->tax_name3 = 'CA Sales Tax';
$quote->tax_rate3 = 5;
}
$quote->save();
$quote_calc = new InvoiceSum($quote);
$quote_calc->build();
$quote = $quote_calc->getQuote();
$quote->save();
$quote->service()->markSent()->save();
$quote->service()->createInvitations();
}
private function buildCreditItem()
{
$line_items = [];
$item = InvoiceItemFactory::create();
$item->quantity = 1;
$item->cost = 1000;
$product = Product::all()->random();
$item->cost = (float) $product->cost;
$item->product_key = $product->product_key;
$item->notes = $product->notes;
$item->custom_value1 = $product->custom_value1;
$item->custom_value2 = $product->custom_value2;
$item->custom_value3 = $product->custom_value3;
$item->custom_value4 = $product->custom_value4;
$line_items[] = $item;
return $line_items;
}
private function buildLineItems($count = 1)
{
$line_items = [];
for ($x = 0; $x < $count; $x++) {
$item = InvoiceItemFactory::create();
$item->quantity = 1;
//$item->cost = 10;
if (rand(0, 1)) {
$item->tax_name1 = 'GST';
$item->tax_rate1 = 10.00;
}
if (rand(0, 1)) {
$item->tax_name1 = 'VAT';
$item->tax_rate1 = 17.50;
}
if (rand(0, 1)) {
$item->tax_name1 = 'Sales Tax';
$item->tax_rate1 = 5;
}
$product = Product::all()->random();
$item->cost = (float) $product->cost;
$item->product_key = $product->product_key;
$item->notes = $product->notes;
$item->custom_value1 = $product->custom_value1;
$item->custom_value2 = $product->custom_value2;
$item->custom_value3 = $product->custom_value3;
$item->custom_value4 = $product->custom_value4;
$line_items[] = $item;
}
return $line_items;
}
private function warmCache()
{
/* Warm up the cache !*/
$cached_tables = config('ninja.cached_tables');
foreach ($cached_tables as $name => $class) {
if (! Cache::has($name)) {
// check that the table exists in case the migration is pending
if (! Schema::hasTable((new $class())->getTable())) {
continue;
}
if ($name == 'payment_terms') {
$orderBy = 'num_days';
} elseif ($name == 'fonts') {
$orderBy = 'sort_order';
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
$orderBy = 'name';
} else {
$orderBy = 'id';
}
$tableData = $class::orderBy($orderBy)->get();
if ($tableData->count()) {
Cache::forever($name, $tableData);
}
}
}
}
private function createGateways($company, $user)
{
if (config('ninja.testvars.stripe') && ($this->gateway == 'all' || $this->gateway == 'stripe')) {
$cg = new CompanyGateway;
$cg->company_id = $company->id;
$cg->user_id = $user->id;
$cg->gateway_key = 'd14dd26a37cecc30fdd65700bfb55b23';
$cg->require_cvv = true;
$cg->require_billing_address = true;
$cg->require_shipping_address = true;
$cg->update_details = true;
$cg->config = encrypt(config('ninja.testvars.stripe'));
$cg->save();
$gateway_types = $cg->driver(new Client)->gatewayTypes();
$fees_and_limits = new stdClass;
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
$cg->fees_and_limits = $fees_and_limits;
$cg->save();
}
if (config('ninja.testvars.paypal') && ($this->gateway == 'all' || $this->gateway == 'paypal')) {
$cg = new CompanyGateway;
$cg->company_id = $company->id;
$cg->user_id = $user->id;
$cg->gateway_key = '38f2c48af60c7dd69e04248cbb24c36e';
$cg->require_cvv = true;
$cg->require_billing_address = true;
$cg->require_shipping_address = true;
$cg->update_details = true;
$cg->config = encrypt(config('ninja.testvars.paypal'));
$cg->save();
$gateway_types = $cg->driver(new Client)->gatewayTypes();
$fees_and_limits = new stdClass;
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
$cg->fees_and_limits = $fees_and_limits;
$cg->save();
}
if (config('ninja.testvars.checkout') && ($this->gateway == 'all' || $this->gateway == 'checkout')) {
$cg = new CompanyGateway;
$cg->company_id = $company->id;
$cg->user_id = $user->id;
$cg->gateway_key = '3758e7f7c6f4cecf0f4f348b9a00f456';
$cg->require_cvv = true;
$cg->require_billing_address = true;
$cg->require_shipping_address = true;
$cg->update_details = true;
$cg->config = encrypt(config('ninja.testvars.checkout'));
$cg->save();
$gateway_types = $cg->driver(new Client)->gatewayTypes();
$fees_and_limits = new stdClass;
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
$cg->fees_and_limits = $fees_and_limits;
$cg->save();
}
if (config('ninja.testvars.authorize') && ($this->gateway == 'all' || $this->gateway == 'authorizenet')) {
$cg = new CompanyGateway;
$cg->company_id = $company->id;
$cg->user_id = $user->id;
$cg->gateway_key = '3b6621f970ab18887c4f6dca78d3f8bb';
$cg->require_cvv = true;
$cg->require_billing_address = true;
$cg->require_shipping_address = true;
$cg->update_details = true;
$cg->config = encrypt(config('ninja.testvars.authorize'));
$cg->save();
$gateway_types = $cg->driver(new Client)->gatewayTypes();
$fees_and_limits = new stdClass;
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
$cg->fees_and_limits = $fees_and_limits;
$cg->save();
}
if (config('ninja.testvars.wepay') && ($this->gateway == 'all' || $this->gateway == 'wepay')) {
$cg = new CompanyGateway;
$cg->company_id = $company->id;
$cg->user_id = $user->id;
$cg->gateway_key = '8fdeed552015b3c7b44ed6c8ebd9e992';
$cg->require_cvv = true;
$cg->require_billing_address = true;
$cg->require_shipping_address = true;
$cg->update_details = true;
$cg->config = encrypt(config('ninja.testvars.wepay'));
$cg->save();
$gateway_types = $cg->driver(new Client)->gatewayTypes();
$fees_and_limits = new stdClass;
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
$cg->fees_and_limits = $fees_and_limits;
$cg->save();
}
if (config('ninja.testvars.braintree') && ($this->gateway == 'all' || $this->gateway == 'braintree')) {
$cg = new CompanyGateway;
$cg->company_id = $company->id;
$cg->user_id = $user->id;
$cg->gateway_key = 'f7ec488676d310683fb51802d076d713';
$cg->require_cvv = true;
$cg->require_billing_address = true;
$cg->require_shipping_address = true;
$cg->update_details = true;
$cg->config = encrypt(config('ninja.testvars.braintree'));
$cg->save();
$gateway_types = $cg->driver(new Client)->gatewayTypes();
$fees_and_limits = new stdClass;
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
$cg->fees_and_limits = $fees_and_limits;
$cg->save();
}
if (config('ninja.testvars.paytrace.decrypted') && ($this->gateway == 'all' || $this->gateway == 'paytrace')) {
$cg = new CompanyGateway;
$cg->company_id = $company->id;
$cg->user_id = $user->id;
$cg->gateway_key = 'bbd736b3254b0aabed6ad7fda1298c88';
$cg->require_cvv = true;
$cg->require_billing_address = true;
$cg->require_shipping_address = true;
$cg->update_details = true;
$cg->config = encrypt(config('ninja.testvars.paytrace.decrypted'));
$cg->save();
$gateway_types = $cg->driver(new Client)->gatewayTypes();
$fees_and_limits = new stdClass;
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
$cg->fees_and_limits = $fees_and_limits;
$cg->save();
}
if (config('ninja.testvars.mollie') && ($this->gateway == 'all' || $this->gateway == 'mollie')) {
$cg = new CompanyGateway;
$cg->company_id = $company->id;
$cg->user_id = $user->id;
$cg->gateway_key = '1bd651fb213ca0c9d66ae3c336dc77e8';
$cg->require_cvv = true;
$cg->require_billing_address = true;
$cg->require_shipping_address = true;
$cg->update_details = true;
$cg->config = encrypt(config('ninja.testvars.mollie'));
$cg->save();
$gateway_types = $cg->driver(new Client)->gatewayTypes();
$fees_and_limits = new stdClass;
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
$cg->fees_and_limits = $fees_and_limits;
$cg->save();
}
}
private function createRecurringInvoice($client)
{
$faker = Factory::create();
$invoice = RecurringInvoiceFactory::create($client->company->id, $client->user->id); //stub the company and user_id
$invoice->client_id = $client->id;
$dateable = Carbon::now()->subDays(rand(0, 90));
$invoice->date = $dateable;
$invoice->line_items = $this->buildLineItems(rand(1, 10));
$invoice->uses_inclusive_taxes = false;
if (rand(0, 1)) {
$invoice->tax_name1 = 'GST';
$invoice->tax_rate1 = 10.00;
}
if (rand(0, 1)) {
$invoice->tax_name2 = 'VAT';
$invoice->tax_rate2 = 17.50;
}
if (rand(0, 1)) {
$invoice->tax_name3 = 'CA Sales Tax';
$invoice->tax_rate3 = 5;
}
$invoice->custom_value1 = $faker->date;
$invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no';
$invoice->status_id = RecurringInvoice::STATUS_ACTIVE;
$invoice->save();
$invoice_calc = new InvoiceSum($invoice);
$invoice_calc->build();
$invoice = $invoice_calc->getInvoice();
$invoice->save();
event(new RecurringInvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars()));
}
}

View file

@ -1,51 +1,29 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Console\Commands;
use App\DataMapper\CompanySettings;
use App\Events\Invoice\InvoiceWasCreated;
use App\Factory\InvoiceFactory;
use App\Factory\InvoiceItemFactory;
use App\Factory\QuoteFactory;
use App\Helpers\Invoice\InvoiceSum;
use App\Models\Account;
use App\Ninja\Repositories\AccountRepository;
use App\Ninja\Repositories\ClientRepository;
use App\Ninja\Repositories\ExpenseRepository;
use App\Ninja\Repositories\InvoiceRepository;
use App\Ninja\Repositories\PaymentRepository;
use App\Ninja\Repositories\VendorRepository;
use App\Ninja\Repositories\TaskRepository;
use App\Ninja\Repositories\ProjectRepository;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Company;
use App\Models\CompanyToken;
use App\Models\Country;
use App\Models\Credit;
use App\Models\Expense;
use App\Models\Product;
use App\Models\TaxRate;
use App\Models\Project;
use App\Models\Quote;
use App\Models\Task;
use App\Models\User;
use App\Models\Vendor;
use App\Models\VendorContact;
use App\Repositories\InvoiceRepository;
use App\Utils\Ninja;
use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesHash;
use Carbon\Carbon;
use App\Models\ExpenseCategory;
use Auth;
use Faker\Factory;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Utils;
/**
* Class CreateTestData.
*/
class CreateTestData extends Command
{
use MakesHash, GeneratesCounter;
/**
* @var string
*/
@ -53,608 +31,284 @@ class CreateTestData extends Command
/**
* @var string
*/
protected $signature = 'ninja:create-test-data {count=1}';
protected $invoice_repo;
protected $signature = 'ninja:create-test-data {count=1} {create_account=false} {--database}';
/**
* Create a new command instance.
*
* @param InvoiceRepository $invoice_repo
* @var
*/
public function __construct(InvoiceRepository $invoice_repo)
protected $token;
/**
* CreateTestData constructor.
*
* @param ClientRepository $clientRepo
* @param InvoiceRepository $invoiceRepo
* @param PaymentRepository $paymentRepo
* @param VendorRepository $vendorRepo
* @param ExpenseRepository $expenseRepo
* @param TaskRepository $taskRepo
* @param AccountRepository $accountRepo
*/
public function __construct(
ClientRepository $clientRepo,
InvoiceRepository $invoiceRepo,
PaymentRepository $paymentRepo,
VendorRepository $vendorRepo,
ExpenseRepository $expenseRepo,
TaskRepository $taskRepo,
ProjectRepository $projectRepo,
AccountRepository $accountRepo)
{
parent::__construct();
$this->invoice_repo = $invoice_repo;
$this->faker = Factory::create();
$this->clientRepo = $clientRepo;
$this->invoiceRepo = $invoiceRepo;
$this->paymentRepo = $paymentRepo;
$this->vendorRepo = $vendorRepo;
$this->expenseRepo = $expenseRepo;
$this->taskRepo = $taskRepo;
$this->projectRepo = $projectRepo;
$this->accountRepo = $accountRepo;
}
/**
* Execute the console command.
*
* @return mixed
* @return bool
*/
public function handle()
{
if (Utils::isNinjaProd()) {
$this->info('Unable to run in production');
return false;
}
$this->info(date('r').' Running CreateTestData...');
$this->count = $this->argument('count');
$this->info('Warming up cache');
if ($database = $this->option('database')) {
config(['database.default' => $database]);
}
$this->warmCache();
if (filter_var($this->argument('create_account'), FILTER_VALIDATE_BOOLEAN)) {
$this->info('Creating new account...');
$account = $this->accountRepo->create(
$this->faker->firstName,
$this->faker->lastName,
$this->faker->safeEmail
);
Auth::login($account->users[0]);
} else {
$this->info('Using first account...');
Auth::loginUsingId(1);
}
$this->createSmallAccount();
$this->createMediumAccount();
$this->createLargeAccount();
$this->createClients();
$this->createVendors();
$this->createOtherObjects();
$this->info('Done');
}
private function createSmallAccount()
private function createClients()
{
$this->info('Creating Small Account and Company');
for ($i = 0; $i < $this->count; $i++) {
$data = [
'name' => $this->faker->name,
'address1' => $this->faker->streetAddress,
'address2' => $this->faker->secondaryAddress,
'city' => $this->faker->city,
'state' => $this->faker->state,
'postal_code' => $this->faker->postcode,
'contacts' => [[
'first_name' => $this->faker->firstName,
'last_name' => $this->faker->lastName,
'email' => $this->faker->safeEmail,
'phone' => $this->faker->phoneNumber,
]],
];
$account = Account::factory()->create();
$company = Company::factory()->create([
'account_id' => $account->id,
'slack_webhook_url' => config('ninja.notification.slack'),
]);
$client = $this->clientRepo->save($data);
$this->info('Client: ' . $client->name);
$account->default_company_id = $company->id;
$account->save();
$user = User::whereEmail('small@example.com')->first();
if (! $user) {
$user = User::factory()->create([
'account_id' => $account->id,
'email' => 'small@example.com',
'confirmation_code' => $this->createDbHash(config('database.default')),
]);
}
$company_token = new CompanyToken;
$company_token->user_id = $user->id;
$company_token->company_id = $company->id;
$company_token->account_id = $account->id;
$company_token->name = 'test token';
$company_token->token = Str::random(64);
$company_token->is_system = true;
$company_token->save();
$user->companies()->attach($company->id, [
'account_id' => $account->id,
'is_owner' => 1,
'is_admin' => 1,
'is_locked' => 0,
'notifications' => CompanySettings::notificationDefaults(),
// 'permissions' => '',
'settings' => null,
]);
Product::factory()->count(50)->create([
'user_id' => $user->id,
'company_id' => $company->id,
]);
$this->info('Creating '.$this->count.' clients');
for ($x = 0; $x < $this->count; $x++) {
$z = $x + 1;
$this->info('Creating client # '.$z);
$this->createClient($company, $user);
}
for ($x = 0; $x < $this->count; $x++) {
$client = $company->clients->random();
$this->info('creating invoice for client #'.$client->id);
$this->createInvoice($client);
$client = $company->clients->random();
$this->info('creating credit for client #'.$client->id);
$this->createCredit($client);
$client = $company->clients->random();
$this->info('creating quote for client #'.$client->id);
$this->createQuote($client);
$client = $company->clients->random();
$this->info('creating expense for client #'.$client->id);
$this->createExpense($client);
$client = $company->clients->random();
$this->info('creating vendor for client #'.$client->id);
$this->createVendor($client);
$client = $company->clients->random();
$this->info('creating task for client #'.$client->id);
$this->createTask($client);
$client = $company->clients->random();
$this->info('creating project for client #'.$client->id);
$this->createProject($client);
$this->createInvoices($client);
$this->createInvoices($client, true);
// $this->createTasks($client);
}
}
private function createMediumAccount()
/**
* @param $client
*/
private function createInvoices($client, $isQuote = false)
{
$this->info('Creating Medium Account and Company');
$account = Account::factory()->create();
$company = Company::factory()->create([
'account_id' => $account->id,
'slack_webhook_url' => config('ninja.notification.slack'),
]);
$account->default_company_id = $company->id;
$account->save();
$user = User::whereEmail('medium@example.com')->first();
if (! $user) {
$user = User::factory()->create([
'account_id' => $account->id,
'email' => 'medium@example.com',
'confirmation_code' => $this->createDbHash(config('database.default')),
]);
}
$company_token = new CompanyToken;
$company_token->user_id = $user->id;
$company_token->company_id = $company->id;
$company_token->account_id = $account->id;
$company_token->name = 'test token';
$company_token->token = Str::random(64);
$company_token->is_system = true;
$company_token->save();
$user->companies()->attach($company->id, [
'account_id' => $account->id,
'is_owner' => 1,
'is_admin' => 1,
'is_locked' => 0,
'notifications' => CompanySettings::notificationDefaults(),
// 'permissions' => '',
'settings' => null,
]);
Product::factory()->count(50)->create([
'user_id' => $user->id,
'company_id' => $company->id,
]);
$this->count = $this->count * 10;
$this->info('Creating '.$this->count.' clients');
for ($x = 0; $x < $this->count; $x++) {
$z = $x + 1;
$this->info('Creating client # '.$z);
$this->createClient($company, $user);
}
for ($x = 0; $x < $this->count * 100; $x++) {
$client = $company->clients->random();
$this->info('creating invoice for client #'.$client->id);
$this->createInvoice($client);
$client = $company->clients->random();
$this->info('creating credit for client #'.$client->id);
$this->createCredit($client);
$client = $company->clients->random();
$this->info('creating quote for client #'.$client->id);
$this->createQuote($client);
$client = $company->clients->random();
$this->info('creating expense for client #'.$client->id);
$this->createExpense($client);
$client = $company->clients->random();
$this->info('creating vendor for client #'.$client->id);
$this->createVendor($client);
$client = $company->clients->random();
$this->info('creating task for client #'.$client->id);
$this->createTask($client);
$client = $company->clients->random();
$this->info('creating project for client #'.$client->id);
$this->createProject($client);
}
}
private function createLargeAccount()
{
$this->info('Creating Large Account and Company');
$account = Account::factory()->create();
$company = Company::factory()->create([
'account_id' => $account->id,
'slack_webhook_url' => config('ninja.notification.slack'),
'is_large' => true,
]);
$account->default_company_id = $company->id;
$account->save();
$user = User::whereEmail('large@example.com')->first();
if (! $user) {
$user = User::factory()->create([
'account_id' => $account->id,
'email' => 'large@example.com',
'confirmation_code' => $this->createDbHash(config('database.default')),
]);
}
$company_token = new CompanyToken;
$company_token->user_id = $user->id;
$company_token->company_id = $company->id;
$company_token->account_id = $account->id;
$company_token->name = 'test token';
$company_token->token = Str::random(64);
$company_token->is_system = true;
$company_token->save();
$user->companies()->attach($company->id, [
'account_id' => $account->id,
'is_owner' => 1,
'is_admin' => 1,
'is_locked' => 0,
'notifications' => CompanySettings::notificationDefaults(),
// 'permissions' => '',
'settings' => null,
]);
Product::factory()->count(15000)->create([
'user_id' => $user->id,
'company_id' => $company->id,
]);
$this->count = $this->count * 10;
$this->info('Creating '.$this->count.' clients');
for ($x = 0; $x < $this->count * 100; $x++) {
$z = $x + 1;
$this->info('Creating client # '.$z);
$this->createClient($company, $user);
}
for ($x = 0; $x < $this->count; $x++) {
$client = $company->clients->random();
$this->info('creating invoice for client #'.$client->id);
$this->createInvoice($client);
$client = $company->clients->random();
$this->info('creating credit for client #'.$client->id);
$this->createCredit($client);
$client = $company->clients->random();
$this->info('creating quote for client #'.$client->id);
$this->createQuote($client);
$client = $company->clients->random();
$this->info('creating expense for client #'.$client->id);
$this->createExpense($client);
$client = $company->clients->random();
$this->info('creating vendor for client #'.$client->id);
$this->createVendor($client);
$client = $company->clients->random();
$this->info('creating task for client #'.$client->id);
$this->createTask($client);
$client = $company->clients->random();
$this->info('creating project for client #'.$client->id);
$this->createProject($client);
}
}
private function createClient($company, $user)
{
// dispatch(function () use ($company, $user) {
// });
$client = Client::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
]);
ClientContact::factory()->create([
'user_id' => $user->id,
'client_id' => $client->id,
'company_id' => $company->id,
'is_primary' => 1,
]);
ClientContact::factory()->count(rand(1, 5))->create([
'user_id' => $user->id,
'client_id' => $client->id,
'company_id' => $company->id,
]);
$client->number = $this->getNextClientNumber($client);
$settings = $client->settings;
$settings->currency_id = (string) rand(1, 79);
$client->settings = $settings;
$country = Country::all()->random();
$client->country_id = $country->id;
$client->save();
}
private function createExpense($client)
{
Expense::factory()->count(rand(1, 5))->create([
'user_id' => $client->user->id,
for ($i = 0; $i < $this->count; $i++) {
$data = [
'is_public' => true,
'is_quote' => $isQuote,
'client_id' => $client->id,
'company_id' => $client->company->id,
]);
'invoice_date_sql' => date_create()->modify(rand(-100, 100) . ' days')->format('Y-m-d'),
'due_date_sql' => date_create()->modify(rand(-100, 100) . ' days')->format('Y-m-d'),
'invoice_items' => [[
'product_key' => $this->faker->word,
'qty' => $this->faker->randomDigit + 1,
'cost' => $this->faker->randomFloat(2, 1, 10),
'notes' => $this->faker->text($this->faker->numberBetween(50, 300)),
]],
];
$invoice = $this->invoiceRepo->save($data);
$this->info('Invoice: ' . $invoice->invoice_number);
if (! $isQuote) {
$this->createPayment($client, $invoice);
}
}
}
private function createVendor($client)
/**
* @param $client
* @param $invoice
*/
private function createPayment($client, $invoice)
{
$vendor = Vendor::factory()->create([
'user_id' => $client->user->id,
'company_id' => $client->company->id,
]);
$data = [
'invoice_id' => $invoice->id,
'client_id' => $client->id,
'amount' => $this->faker->randomFloat(2, 0, $invoice->amount),
'payment_date_sql' => date_create()->modify(rand(-100, 100) . ' days')->format('Y-m-d'),
];
VendorContact::factory()->create([
'user_id' => $client->user->id,
$payment = $this->paymentRepo->save($data);
$this->info('Payment: ' . $payment->amount);
}
private function createTasks($client)
{
$data = [
'client_id' => $client->id,
'name' => $this->faker->sentence(3),
];
$project = $this->projectRepo->save($data);
for ($i = 0; $i < $this->count; $i++) {
$startTime = date_create()->modify(rand(-100, 100) . ' days')->format('U');
$endTime = $startTime + (60 * 60 * 2);
$timeLog = "[[{$startTime},{$endTime}]]";
$data = [
'client_id' => $client->id,
'project_id' => $project->id,
'description' => $this->faker->text($this->faker->numberBetween(50, 300)),
'time_log' => $timeLog,
];
$this->taskRepo->save(false, $data);
}
}
private function createVendors()
{
for ($i = 0; $i < $this->count; $i++) {
$data = [
'name' => $this->faker->name,
'address1' => $this->faker->streetAddress,
'address2' => $this->faker->secondaryAddress,
'city' => $this->faker->city,
'state' => $this->faker->state,
'postal_code' => $this->faker->postcode,
'vendor_contacts' => [[
'first_name' => $this->faker->firstName,
'last_name' => $this->faker->lastName,
'email' => $this->faker->safeEmail,
'phone' => $this->faker->phoneNumber,
]],
];
$vendor = $this->vendorRepo->save($data);
$this->info('Vendor: ' . $vendor->name);
$this->createExpense($vendor);
}
}
/**
* @param $vendor
*/
private function createExpense($vendor)
{
for ($i = 0; $i < $this->count; $i++) {
$data = [
'vendor_id' => $vendor->id,
'company_id' => $client->company->id,
'is_primary' => 1,
]);
'amount' => $this->faker->randomFloat(2, 1, 10),
'expense_date' => date_create()->modify(rand(-100, 100) . ' days')->format('Y-m-d'),
'public_notes' => '',
];
VendorContact::factory()->count(rand(1, 5))->create([
'user_id' => $client->user->id,
'vendor_id' => $vendor->id,
'company_id' => $client->company->id,
'is_primary' => 0,
]);
$expense = $this->expenseRepo->save($data);
$this->info('Expense: ' . $expense->amount);
}
}
private function createTask($client)
private function createOtherObjects()
{
$vendor = Task::factory()->create([
'user_id' => $client->user->id,
'company_id' => $client->company->id,
]);
$this->createTaxRate('Tax 1', 10, 1);
$this->createTaxRate('Tax 2', 20, 2);
$this->createCategory('Category 1', 1);
$this->createCategory('Category 1', 2);
$this->createProject('Project 1', 1);
$this->createProject('Project 2', 2);
}
private function createProject($client)
private function createTaxRate($name, $rate, $publicId)
{
$vendor = Project::factory()->create([
'user_id' => $client->user->id,
'company_id' => $client->company->id,
]);
$taxRate = new TaxRate();
$taxRate->name = $name;
$taxRate->rate = $rate;
$taxRate->account_id = 1;
$taxRate->user_id = 1;
$taxRate->public_id = $publicId;
$taxRate->save();
}
private function createInvoice($client)
private function createCategory($name, $publicId)
{
$faker = Factory::create();
$invoice = InvoiceFactory::create($client->company->id, $client->user->id); //stub the company and user_id
$invoice->client_id = $client->id;
// $invoice->date = $faker->date();
$dateable = Carbon::now()->subDays(rand(0, 90));
$invoice->date = $dateable;
$invoice->line_items = $this->buildLineItems(rand(1, 10));
$invoice->uses_inclusive_taxes = false;
if (rand(0, 1)) {
$invoice->tax_name1 = 'GST';
$invoice->tax_rate1 = 10.00;
}
if (rand(0, 1)) {
$invoice->tax_name2 = 'VAT';
$invoice->tax_rate2 = 17.50;
}
if (rand(0, 1)) {
$invoice->tax_name3 = 'CA Sales Tax';
$invoice->tax_rate3 = 5;
}
$invoice->custom_value1 = $faker->date;
$invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no';
$invoice->save();
$invoice_calc = new InvoiceSum($invoice);
$invoice_calc->build();
$invoice = $invoice_calc->getInvoice();
$invoice->save();
$invoice->service()->createInvitations()->markSent();
$this->invoice_repo->markSent($invoice);
if (rand(0, 1)) {
$invoice = $invoice->service()->markPaid()->save();
}
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars()));
$category = new ExpenseCategory();
$category->name = $name;
$category->account_id = 1;
$category->user_id = 1;
$category->public_id = $publicId;
$category->save();
}
private function createCredit($client)
private function createProject($name, $publicId)
{
$faker = Factory::create();
$credit = Credit::factory()->create(['user_id' => $client->user->id, 'company_id' => $client->company->id, 'client_id' => $client->id]);
$dateable = Carbon::now()->subDays(rand(0, 90));
$credit->date = $dateable;
$credit->line_items = $this->buildLineItems(rand(1, 10));
$credit->uses_inclusive_taxes = false;
if (rand(0, 1)) {
$credit->tax_name1 = 'GST';
$credit->tax_rate1 = 10.00;
}
if (rand(0, 1)) {
$credit->tax_name2 = 'VAT';
$credit->tax_rate2 = 17.50;
}
if (rand(0, 1)) {
$credit->tax_name3 = 'CA Sales Tax';
$credit->tax_rate3 = 5;
}
$credit->save();
$invoice_calc = new InvoiceSum($credit);
$invoice_calc->build();
$credit = $invoice_calc->getCredit();
$credit->save();
$credit->service()->markSent()->save();
$credit->service()->createInvitations();
$project = new Project();
$project->name = $name;
$project->account_id = 1;
$project->client_id = 1;
$project->user_id = 1;
$project->public_id = $publicId;
$project->save();
}
private function createQuote($client)
/**
* @return array
*/
protected function getArguments()
{
$faker = Factory::create();
//$quote = QuoteFactory::create($client->company->id, $client->user->id);//stub the company and user_id
$quote = Quote::factory()->create(['user_id' => $client->user->id, 'company_id' => $client->company->id, 'client_id' => $client->id]);
$quote->date = $faker->date();
$quote->client_id = $client->id;
$quote->setRelation('client', $client);
$quote->line_items = $this->buildLineItems(rand(1, 10));
$quote->uses_inclusive_taxes = false;
if (rand(0, 1)) {
$quote->tax_name1 = 'GST';
$quote->tax_rate1 = 10.00;
}
if (rand(0, 1)) {
$quote->tax_name2 = 'VAT';
$quote->tax_rate2 = 17.50;
}
if (rand(0, 1)) {
$quote->tax_name3 = 'CA Sales Tax';
$quote->tax_rate3 = 5;
}
$quote->save();
$quote_calc = new InvoiceSum($quote);
$quote_calc->build();
$quote = $quote_calc->getQuote();
$quote->save();
$quote->service()->markSent()->save();
$quote->service()->createInvitations();
return [];
}
private function buildLineItems($count = 1)
/**
* @return array
*/
protected function getOptions()
{
$line_items = [];
for ($x = 0; $x < $count; $x++) {
$item = InvoiceItemFactory::create();
$item->quantity = 1;
//$item->cost = 10;
if (rand(0, 1)) {
$item->tax_name1 = 'GST';
$item->tax_rate1 = 10.00;
}
if (rand(0, 1)) {
$item->tax_name1 = 'VAT';
$item->tax_rate1 = 17.50;
}
if (rand(0, 1)) {
$item->tax_name1 = 'Sales Tax';
$item->tax_rate1 = 5;
}
$product = Product::all()->random();
$item->cost = (float) $product->cost;
$item->product_key = $product->product_key;
$item->notes = $product->notes;
$item->custom_value1 = $product->custom_value1;
$item->custom_value2 = $product->custom_value2;
$item->custom_value3 = $product->custom_value3;
$item->custom_value4 = $product->custom_value4;
$line_items[] = $item;
}
return $line_items;
}
private function warmCache()
{
/* Warm up the cache !*/
$cached_tables = config('ninja.cached_tables');
foreach ($cached_tables as $name => $class) {
if (! Cache::has($name)) {
// check that the table exists in case the migration is pending
if (! Schema::hasTable((new $class())->getTable())) {
continue;
}
if ($name == 'payment_terms') {
$orderBy = 'num_days';
} elseif ($name == 'fonts') {
$orderBy = 'sort_order';
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
$orderBy = 'name';
} else {
$orderBy = 'id';
}
$tableData = $class::orderBy($orderBy)->get();
if ($tableData->count()) {
Cache::forever($name, $tableData);
}
}
}
return [];
}
}

View file

@ -1,664 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Console\Commands;
use App\DataMapper\CompanySettings;
use App\Events\Invoice\InvoiceWasCreated;
use App\Factory\InvoiceFactory;
use App\Factory\InvoiceItemFactory;
use App\Factory\RecurringInvoiceFactory;
use App\Helpers\Invoice\InvoiceSum;
use App\Jobs\Company\CreateCompanyPaymentTerms;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Jobs\Ninja\CompanySizeCheck;
use App\Jobs\Util\VersionCheck;
use App\Models\Account;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Company;
use App\Models\CompanyToken;
use App\Models\Credit;
use App\Models\Expense;
use App\Models\Product;
use App\Models\Project;
use App\Models\Quote;
use App\Models\RecurringInvoice;
use App\Models\Task;
use App\Models\TaskStatus;
use App\Models\User;
use App\Models\Vendor;
use App\Models\VendorContact;
use App\Repositories\InvoiceRepository;
use App\Utils\Ninja;
use App\Utils\Traits\GeneratesCounter;
use App\Utils\Traits\MakesHash;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
class DemoMode extends Command
{
use MakesHash, GeneratesCounter;
protected $name = 'ninja:demo-mode';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:demo-mode';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Setup demo mode';
protected $invoice_repo;
public function __construct(InvoiceRepository $invoice_repo)
{
parent::__construct();
$this->invoice_repo = $invoice_repo;
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
set_time_limit(0);
$cached_tables = config('ninja.cached_tables');
foreach ($cached_tables as $name => $class) {
if (! Cache::has($name)) {
// check that the table exists in case the migration is pending
if (! Schema::hasTable((new $class())->getTable())) {
continue;
}
if ($name == 'payment_terms') {
$orderBy = 'num_days';
} elseif ($name == 'fonts') {
$orderBy = 'sort_order';
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
$orderBy = 'name';
} else {
$orderBy = 'id';
}
$tableData = $class::orderBy($orderBy)->get();
if ($tableData->count()) {
Cache::forever($name, $tableData);
}
}
}
$this->info('Migrating');
Artisan::call('migrate:fresh --force');
$this->info('Seeding');
Artisan::call('db:seed --force');
$this->info('Seeding Random Data');
$this->createSmallAccount();
VersionCheck::dispatchNow();
CompanySizeCheck::dispatchNow();
}
private function createSmallAccount()
{
$faker = \Faker\Factory::create();
$this->count = 25;
$this->info('Creating Small Account and Company');
$account = Account::factory()->create();
$company = Company::factory()->create([
'account_id' => $account->id,
'slack_webhook_url' => config('ninja.notification.slack'),
'enabled_modules' => 32767,
'company_key' => 'KEY',
'enable_shop_api' => true,
]);
$settings = $company->settings;
$settings->name = $faker->company;
$settings->address1 = $faker->buildingNumber;
$settings->address2 = $faker->streetAddress;
$settings->city = $faker->city;
$settings->state = $faker->state;
$settings->postal_code = $faker->postcode;
$settings->website = $faker->url;
$settings->vat_number = (string) $faker->numberBetween(123456789, 987654321);
$settings->phone = (string) $faker->phoneNumber;
$company->settings = $settings;
$company->save();
$account->default_company_id = $company->id;
$account->save();
$user = User::whereEmail('small@example.com')->first();
if (! $user) {
$user = User::factory()->create([
'account_id' => $account->id,
'email' => 'small@example.com',
'confirmation_code' => $this->createDbHash(config('database.default')),
'email_verified_at' => now(),
]);
}
CreateCompanyPaymentTerms::dispatchNow($company, $user);
CreateCompanyTaskStatuses::dispatchNow($company, $user);
$company_token = new CompanyToken;
$company_token->user_id = $user->id;
$company_token->company_id = $company->id;
$company_token->account_id = $account->id;
$company_token->name = 'test token';
$company_token->token = Str::random(64);
$company_token->is_system = true;
$company_token->save();
$user->companies()->attach($company->id, [
'account_id' => $account->id,
'is_owner' => 1,
'is_admin' => 1,
'is_locked' => 0,
'notifications' => CompanySettings::notificationDefaults(),
// 'permissions' => '',
'settings' => null,
]);
$u2 = User::where('email', 'demo@invoiceninja.com')->first();
if (! $u2) {
$u2 = User::factory()->create([
'email' => 'demo@invoiceninja.com',
'password' => Hash::make('Password0'),
'account_id' => $account->id,
'confirmation_code' => $this->createDbHash(config('database.default')),
'email_verified_at' => now(),
]);
$company_token = new CompanyToken;
$company_token->user_id = $u2->id;
$company_token->company_id = $company->id;
$company_token->account_id = $account->id;
$company_token->name = 'test token';
$company_token->token = 'TOKEN';
$company_token->save();
$u2->companies()->attach($company->id, [
'account_id' => $account->id,
'is_owner' => 1,
'is_admin' => 1,
'is_locked' => 0,
'notifications' => CompanySettings::notificationDefaults(),
'permissions' => '',
'settings' => null,
]);
}
Product::factory()->count(50)->create([
'user_id' => $user->id,
'company_id' => $company->id,
]);
$this->info('Creating '.$this->count.' clients');
for ($x = 0; $x < $this->count; $x++) {
$z = $x + 1;
$this->info('Creating client # '.$z);
$this->createClient($company, $user, $u2->id);
}
for ($x = 0; $x < $this->count; $x++) {
$client = $company->clients->random();
$this->info('creating entities for client #'.$client->id);
$this->createInvoice($client, $u2->id);
$this->createRecurringInvoice($client, $u2->id);
$client = $company->clients->random();
$this->createCredit($client, $u2->id);
$client = $company->clients->random();
$this->createQuote($client, $u2->id);
$client = $company->clients->random();
$this->createExpense($client);
$client = $company->clients->random();
$this->createVendor($client, $u2->id);
$client = $company->clients->random();
$this->createTask($client, $u2->id);
$client = $company->clients->random();
$this->createProject($client, $u2->id);
}
}
private function createClient($company, $user, $assigned_user_id = null)
{
// dispatch(function () use ($company, $user) {
// });
$client = Client::factory()->create([
'user_id' => $user->id,
'company_id' => $company->id,
]);
ClientContact::factory()->create([
'user_id' => $user->id,
'client_id' => $client->id,
'company_id' => $company->id,
'is_primary' => 1,
]);
ClientContact::factory()->count(rand(1, 5))->create([
'user_id' => $user->id,
'client_id' => $client->id,
'company_id' => $company->id,
]);
$client->number = $this->getNextClientNumber($client);
$settings = $client->settings;
$settings->currency_id = (string) rand(1, 3);
$client->settings = $settings;
if (rand(0, 1)) {
$client->assigned_user_id = $assigned_user_id;
}
$client->country_id = array_rand([36, 392, 840, 124, 276, 826]);
$client->save();
}
private function createExpense($client)
{
Expense::factory()->count(rand(1, 5))->create([
'user_id' => $client->user_id,
'client_id' => $client->id,
'company_id' => $client->company_id,
]);
Expense::all()->each(function ($expense){
$expense->number = $this->getNextExpenseNumber($expense);
$expense->save();
});
}
private function createVendor($client, $assigned_user_id = null)
{
$vendor = Vendor::factory()->create([
'user_id' => $client->user_id,
'company_id' => $client->company_id,
]);
VendorContact::factory()->create([
'user_id' => $client->user->id,
'vendor_id' => $vendor->id,
'company_id' => $client->company_id,
'is_primary' => 1,
]);
VendorContact::factory()->count(rand(1, 5))->create([
'user_id' => $client->user->id,
'vendor_id' => $vendor->id,
'company_id' => $client->company_id,
'is_primary' => 0,
]);
$vendor->number = $this->getNextVendorNumber($vendor);
$vendor->save();
}
private function createTask($client, $assigned_user_id = null)
{
$task = Task::factory()->create([
'user_id' => $client->user->id,
'company_id' => $client->company_id,
'client_id' => $client->id
]);
$task->status_id = TaskStatus::all()->random()->id;
$task->number = $this->getNextTaskNumber($task);
$task->save();
}
private function createProject($client, $assigned_user_id = null)
{
$project = Project::factory()->create([
'user_id' => $client->user->id,
'company_id' => $client->company_id,
'client_id' => $client->id,
]);
$project->number = $this->getNextProjectNumber($project);
$project->save();
}
private function createInvoice($client, $assigned_user_id = null)
{
$faker = \Faker\Factory::create();
$invoice = InvoiceFactory::create($client->company->id, $client->user->id); //stub the company and user_id
$invoice->client_id = $client->id;
if ((bool) rand(0, 1)) {
$dateable = Carbon::now()->subDays(rand(0, 90));
} else {
$dateable = Carbon::now()->addDays(rand(0, 90));
}
$invoice->date = $dateable;
$invoice->line_items = $this->buildLineItems(rand(1, 10));
$invoice->uses_inclusive_taxes = false;
// if (rand(0, 1)) {
// $invoice->tax_name1 = 'GST';
// $invoice->tax_rate1 = 10.00;
// }
// if (rand(0, 1)) {
// $invoice->tax_name2 = 'VAT';
// $invoice->tax_rate2 = 17.50;
// }
// if (rand(0, 1)) {
// $invoice->tax_name3 = 'CA Sales Tax';
// $invoice->tax_rate3 = 5;
// }
// $invoice->custom_value1 = $faker->date;
// $invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no';
$invoice->save();
$invoice_calc = new InvoiceSum($invoice);
$invoice_calc->build();
$invoice = $invoice_calc->getInvoice();
if (rand(0, 1)) {
$invoice->assigned_user_id = $assigned_user_id;
}
$invoice->save();
$invoice->service()->createInvitations()->markSent();
$this->invoice_repo->markSent($invoice);
if ((bool) rand(0, 2)) {
$invoice = $invoice->service()->markPaid()->save();
$invoice->payments->each(function ($payment) {
$payment->date = now()->addDays(rand(-30, 30));
$payment->save();
});
}
event(new InvoiceWasCreated($invoice, $invoice->company, Ninja::eventVars()));
}
private function createRecurringInvoice($client, $assigned_user_id = null)
{
$faker = \Faker\Factory::create();
$invoice = RecurringInvoiceFactory::create($client->company->id, $client->user->id); //stub the company and user_id
$invoice->client_id = $client->id;
$invoice->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
$invoice->last_sent_date = now()->subMonth();
$invoice->next_send_date = now()->addMonthNoOverflow();
if ((bool) rand(0, 1)) {
$dateable = Carbon::now()->subDays(rand(0, 90));
} else {
$dateable = Carbon::now()->addDays(rand(0, 90));
}
$invoice->date = $dateable;
$invoice->line_items = $this->buildLineItems(rand(1, 10));
$invoice->uses_inclusive_taxes = false;
// if (rand(0, 1)) {
// $invoice->tax_name1 = 'GST';
// $invoice->tax_rate1 = 10.00;
// }
// if (rand(0, 1)) {
// $invoice->tax_name2 = 'VAT';
// $invoice->tax_rate2 = 17.50;
// }
// if (rand(0, 1)) {
// $invoice->tax_name3 = 'CA Sales Tax';
// $invoice->tax_rate3 = 5;
// }
// $invoice->custom_value1 = $faker->date;
// $invoice->custom_value2 = rand(0, 1) ? 'yes' : 'no';
$invoice->save();
$invoice_calc = new InvoiceSum($invoice);
$invoice_calc->build();
$invoice = $invoice_calc->getInvoice();
if (rand(0, 1)) {
$invoice->assigned_user_id = $assigned_user_id;
}
$invoice->number = $this->getNextRecurringInvoiceNumber($client);
$invoice->save();
}
private function createCredit($client, $assigned_user_id = null)
{
$faker = \Faker\Factory::create();
$credit = Credit::factory()->create(['user_id' => $client->user->id, 'company_id' => $client->company->id, 'client_id' => $client->id]);
if ((bool) rand(0, 1)) {
$dateable = Carbon::now()->subDays(rand(0, 90));
} else {
$dateable = Carbon::now()->addDays(rand(0, 90));
}
$credit->date = $dateable;
$credit->line_items = $this->buildLineItems(rand(1, 10));
$credit->uses_inclusive_taxes = false;
// if (rand(0, 1)) {
// $credit->tax_name1 = 'GST';
// $credit->tax_rate1 = 10.00;
// }
// if (rand(0, 1)) {
// $credit->tax_name2 = 'VAT';
// $credit->tax_rate2 = 17.50;
// }
// if (rand(0, 1)) {
// $credit->tax_name3 = 'CA Sales Tax';
// $credit->tax_rate3 = 5;
// }
$credit->save();
$invoice_calc = new InvoiceSum($credit);
$invoice_calc->build();
$credit = $invoice_calc->getCredit();
if (rand(0, 1)) {
$credit->assigned_user_id = $assigned_user_id;
}
$credit->save();
$credit->service()->markSent()->save();
$credit->service()->createInvitations();
}
private function createQuote($client, $assigned_user_id = null)
{
$faker = \Faker\Factory::create();
$quote = Quote::factory()->create(['user_id' => $client->user->id, 'company_id' => $client->company_id, 'client_id' => $client->id]);
if ((bool) rand(0, 1)) {
$dateable = Carbon::now()->subDays(rand(1, 30));
$dateable_due = $dateable->addDays(rand(1, 30));
} else {
$dateable = Carbon::now()->addDays(rand(1, 30));
$dateable_due = $dateable->addDays(rand(-10, 30));
}
$quote->date = $dateable;
$quote->due_date = $dateable_due;
$quote->client_id = $client->id;
$quote->setRelation('client', $client);
$quote->line_items = $this->buildLineItems(rand(1, 10));
$quote->uses_inclusive_taxes = false;
// if (rand(0, 1)) {
// $quote->tax_name1 = 'GST';
// $quote->tax_rate1 = 10.00;
// }
// if (rand(0, 1)) {
// $quote->tax_name2 = 'VAT';
// $quote->tax_rate2 = 17.50;
// }
// if (rand(0, 1)) {
// $quote->tax_name3 = 'CA Sales Tax';
// $quote->tax_rate3 = 5;
// }
$quote->save();
$quote_calc = new InvoiceSum($quote);
$quote_calc->build();
$quote = $quote_calc->getQuote();
if (rand(0, 1)) {
$quote->assigned_user_id = $assigned_user_id;
}
$quote->save();
$quote->service()->markSent()->save();
$quote->service()->createInvitations();
}
private function buildLineItems($count = 1)
{
$line_items = [];
for ($x = 0; $x < $count; $x++) {
$item = InvoiceItemFactory::create();
$item->quantity = 1;
//$item->cost = 10;
// if (rand(0, 1)) {
// $item->tax_name1 = 'GST';
// $item->tax_rate1 = 10.00;
// }
// if (rand(0, 1)) {
// $item->tax_name1 = 'VAT';
// $item->tax_rate1 = 17.50;
// }
// if (rand(0, 1)) {
// $item->tax_name1 = 'Sales Tax';
// $item->tax_rate1 = 5;
// }
$product = Product::all()->random();
$item->cost = (float) $product->cost;
$item->product_key = $product->product_key;
$item->notes = $product->notes;
$item->custom_value1 = $product->custom_value1;
$item->custom_value2 = $product->custom_value2;
$item->custom_value3 = $product->custom_value3;
$item->custom_value4 = $product->custom_value4;
$line_items[] = $item;
}
return $line_items;
}
private function warmCache()
{
/* Warm up the cache !*/
$cached_tables = config('ninja.cached_tables');
foreach ($cached_tables as $name => $class) {
if (! Cache::has($name)) {
// check that the table exists in case the migration is pending
if (! Schema::hasTable((new $class())->getTable())) {
continue;
}
if ($name == 'payment_terms') {
$orderBy = 'num_days';
} elseif ($name == 'fonts') {
$orderBy = 'sort_order';
} elseif (in_array($name, ['currencies', 'industries', 'languages', 'countries', 'banks'])) {
$orderBy = 'name';
} else {
$orderBy = 'id';
}
$tableData = $class::orderBy($orderBy)->get();
if ($tableData->count()) {
Cache::forever($name, $tableData);
}
}
}
}
}

View file

@ -1,92 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Console\Commands;
use App\Libraries\MultiDB;
use App\Models\Design;
use Illuminate\Console\Command;
use stdClass;
class DesignUpdate extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:design-update';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Update the system designs when changes are made.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
//always return state to first DB
$current_db = config('database.default');
if (! config('ninja.db.multi_db_enabled')) {
$this->handleOnDb();
} else {
//multiDB environment, need to
foreach (MultiDB::$dbs as $db) {
MultiDB::setDB($db);
$this->handleOnDb($db);
}
MultiDB::setDB($current_db);
}
}
private function handleOnDb()
{
foreach (Design::whereIsCustom(false)->get() as $design) {
$invoice_design = new \App\Services\PdfMaker\Design(strtolower($design->name));
$invoice_design->document();
$design_object = new stdClass;
$design_object->includes = $invoice_design->getSectionHTML('style');
$design_object->header = $invoice_design->getSectionHTML('header');
$design_object->body = $invoice_design->getSectionHTML('body');
$design_object->product = '';
$design_object->task = '';
$design_object->footer = $invoice_design->getSectionHTML('footer');
$design->design = $design_object;
$design->save();
}
}
}

View file

@ -0,0 +1,115 @@
<?php
namespace App\Console\Commands;
use App\Libraries\Utils;
use App\Models\User;
use App\Traits\GenerateMigrationResources;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Auth;
class ExportMigrations extends Command
{
use GenerateMigrationResources;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'migrations:export {--user=} {--random=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Export account migrations to folder.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->info('Note: Migrations will be stored inside of (storage/migrations) folder.');
if($this->option('user')) {
$record = User::findOrFail($this->option('user'));
return $this->export($record);
}
if($this->option('random')){
User::all()->random(200)->each(function ($user){
$this->export($user);
});
return;
}
$users = User::all();
foreach($users as $user) {
Auth::login($user);
$this->export($user);
}
}
private function export($user)
{
$this->account = $user->account;
$date = date('Y-m-d');
$accountKey = $this->account->account_key;
$output = fopen('php://output', 'w') or Utils::fatalError();
$fileName = "{$accountKey}-{$date}-invoiceninja";
$data['data'] = [
'account' => $this->getAccount(),
'company' => $this->getCompany(),
'users' => $this->getUsers(),
'tax_rates' => $this->getTaxRates(),
'payment_terms' => $this->getPaymentTerms(),
'clients' => $this->getClients(),
'company_gateways' => $this->getCompanyGateways(),
'client_gateway_tokens' => $this->getClientGatewayTokens(),
'vendors' => $this->getVendors(),
'projects' => $this->getProjects(),
'products' => $this->getProducts(),
'credits' => $this->getCreditsNotes(),
'invoices' => $this->getInvoices(),
'recurring_invoices' => $this->getRecurringInvoices(),
'quotes' => $this->getQuotes(),
'payments' => array_merge($this->getPayments(), $this->getCredits()),
'documents' => $this->getDocuments(),
'expense_categories' => $this->getExpenseCategories(),
'task_statuses' => $this->getTaskStatuses(),
'expenses' => $this->getExpenses(),
'tasks' => $this->getTasks(),
'documents' => $this->getDocuments(),
];
$file = storage_path("migrations/{$fileName}.zip");
$zip = new \ZipArchive();
$zip->open($file, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
$zip->addFromString('migration.json', json_encode($data, JSON_PRETTY_PRINT));
$zip->close();
$this->info('User with id #' . $user->id . ' exported.');
}
}

View file

@ -1,61 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\ClientContact;
use App\Models\Company;
use App\Models\User;
use App\Utils\Ninja;
use Illuminate\Console\Command;
class HostedUsers extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:sync-users';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Syncs Invoice Ninja Users';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
Company::on('db-ninja-01')->each(function ($company){
if(Ninja::isHosted())
\Modules\Admin\Jobs\Account\NinjaUser::dispatchNow([], $company);
});
Company::on('db-ninja-02')->each(function ($company){
if(Ninja::isHosted())
\Modules\Admin\Jobs\Account\NinjaUser::dispatchNow([], $company);
});
}
}

View file

@ -1,175 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Console\Commands;
use App\DataMapper\CompanySettings;
use App\Exceptions\MigrationValidatorFailed;
use App\Exceptions\NonExistingMigrationFile;
use App\Exceptions\ProcessingMigrationArchiveFailed;
use App\Exceptions\ResourceDependencyMissing;
use App\Exceptions\ResourceNotAvailableForMigration;
use App\Jobs\Util\Import;
use App\Jobs\Util\StartMigration;
use App\Mail\MigrationFailed;
use App\Models\Account;
use App\Models\Company;
use App\Models\CompanyToken;
use App\Models\User;
use App\Utils\Traits\AppSetup;
use App\Utils\Traits\MakesHash;
use DirectoryIterator;
use Faker\Factory;
use Faker\Generator;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
use ZipArchive;
class ImportMigrations extends Command
{
use MakesHash;
use AppSetup;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'migrations:import {--path=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Massively import the migrations.';
/**
* @var Generator
*/
private $faker;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
$this->faker = Factory::create();
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->buildCache();
$path = $this->option('path') ?? public_path('storage/migrations/import');
nlog(public_path('storage/migrations/import'));
$directory = new DirectoryIterator($path);
foreach ($directory as $file) {
if ($file->getExtension() === 'zip') {
$user = $this->getUser();
$company = $this->getUser()->companies()->first();
$this->info('Started processing: '.$file->getBasename().' at '.now());
$zip = new ZipArchive();
$archive = $zip->open($file->getRealPath());
try {
if (! $archive) {
throw new ProcessingMigrationArchiveFailed('Processing migration archive failed. Migration file is possibly corrupted.');
}
$filename = pathinfo($file->getRealPath(), PATHINFO_FILENAME);
$zip->extractTo(public_path("storage/migrations/{$filename}"));
$zip->close();
$import_file = public_path("storage/migrations/$filename/migration.json");
Import::dispatch($import_file, $this->getUser()->companies()->first(), $this->getUser());
// StartMigration::dispatch($file->getRealPath(), $this->getUser(), $this->getUser()->companies()->first());
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) {
\Mail::to($this->user)->send(new MigrationFailed($e, $e->getMessage()));
if (app()->environment() !== 'production') {
info($e->getMessage());
}
}
}
}
}
public function getUser(): User
{
$account = $this->getAccount();
$company = $this->getCompany($account);
$user = User::factory()->create([
'account_id' => $account->id,
'email' => Str::random(10) . "@example.com",
'confirmation_code' => $this->createDbHash(config('database.default')),
]);
CompanyToken::unguard();
$company_token = CompanyToken::create([
'user_id' => $user->id,
'company_id' => $company->id,
'account_id' => $account->id,
'name' => 'First token',
'token' => Str::random(64),
]);
$user->companies()->attach($company->id, [
'account_id' => $account->id,
'is_owner' => 1,
'is_admin' => 1,
'is_locked' => 0,
'notifications' => CompanySettings::notificationDefaults(),
'permissions' => '',
'settings' => null,
]);
return $user;
}
public function getAccount(): Account
{
return Account::factory()->create();
}
public function getCompany(Account $account): Company
{
$company = Company::factory()->create([
'account_id' => $account->id,
'is_disabled' => true,
]);
if (! $account->default_company_id) {
$account->default_company_id = $company->id;
$account->save();
}
return $company;
}
}

View file

@ -0,0 +1,380 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use DB;
use Mail;
use Exception;
use App\Models\DbServer;
use App\Models\LookupCompany;
use App\Models\LookupAccount;
use App\Models\LookupUser;
use App\Models\LookupContact;
use App\Models\LookupAccountToken;
use App\Models\LookupInvitation;
class InitLookup extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:init-lookup {--truncate=} {--subdomain} {--validate=} {--update=} {--company_id=} {--page_size=100} {--database=db-ninja-1}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Initialize lookup tables';
protected $log = '';
protected $isValid = true;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->logMessage('Running InitLookup...');
config(['database.default' => DB_NINJA_LOOKUP]);
$database = $this->option('database');
$dbServer = DbServer::whereName($database)->first();
if ($this->option('subdomain')) {
$this->logMessage('Updating subdomains...');
$this->popuplateSubdomains();
} else if ($this->option('truncate')) {
$this->logMessage('Truncating data...');
$this->truncateTables();
} else {
config(['database.default' => $this->option('database')]);
$count = DB::table('companies')
->where('id', '>=', $this->option('company_id') ?: 1)
->count();
for ($i=0; $i<$count; $i += (int) $this->option('page_size')) {
$this->initCompanies($dbServer->id, $i);
}
}
$this->logMessage('Results: ' . ($this->isValid ? RESULT_SUCCESS : RESULT_FAILURE));
if ($this->option('validate')) {
if ($errorEmail = env('ERROR_EMAIL')) {
Mail::raw($this->log, function ($message) use ($errorEmail, $database) {
$message->to($errorEmail)
->from(CONTACT_EMAIL)
->subject("Check-Lookups [{$database}]: " . strtoupper($this->isValid ? RESULT_SUCCESS : RESULT_FAILURE));
});
} elseif (! $this->isValid) {
throw new Exception('Check lookups failed!!');
}
}
}
private function popuplateSubdomains()
{
$data = [];
config(['database.default' => $this->option('database')]);
$accounts = DB::table('accounts')
->orderBy('id')
->where('subdomain', '!=', '')
->get(['account_key', 'subdomain']);
foreach ($accounts as $account) {
$data[$account->account_key] = $account->subdomain;
}
config(['database.default' => DB_NINJA_LOOKUP]);
$validate = $this->option('validate');
$update = $this->option('update');
foreach ($data as $accountKey => $subdomain) {
LookupAccount::whereAccountKey($accountKey)->update(['subdomain' => $subdomain]);
}
}
private function initCompanies($dbServerId, $offset = 0)
{
$data = [];
config(['database.default' => $this->option('database')]);
$companies = DB::table('companies')
->offset($offset)
->limit((int) $this->option('page_size'))
->orderBy('id')
->where('id', '>=', $this->option('company_id') ?: 1)
->get(['id']);
foreach ($companies as $company) {
$data[$company->id] = $this->parseCompany($company->id);
}
config(['database.default' => DB_NINJA_LOOKUP]);
$validate = $this->option('validate');
$update = $this->option('update');
foreach ($data as $companyId => $company) {
$lookupCompany = false;
if ($validate || $update) {
$lookupCompany = LookupCompany::whereDbServerId($dbServerId)->whereCompanyId($companyId)->first();
}
if ($validate && ! $lookupCompany) {
$this->logError("LookupCompany - dbServerId: {$dbServerId}, companyId: {$companyId} | Not found!");
continue;
}
if (! $lookupCompany) {
$lookupCompany = LookupCompany::create([
'db_server_id' => $dbServerId,
'company_id' => $companyId,
]);
}
foreach ($company as $accountKey => $account) {
$lookupAccount = false;
if ($validate || $update) {
$lookupAccount = LookupAccount::whereLookupCompanyId($lookupCompany->id)->whereAccountKey($accountKey)->first();
}
if ($validate && ! $lookupAccount) {
$this->logError("LookupAccount - lookupCompanyId: {$lookupCompany->id}, accountKey {$accountKey} | Not found!");
continue;
}
if (! $lookupAccount) {
$lookupAccount = LookupAccount::create([
'lookup_company_id' => $lookupCompany->id,
'account_key' => $accountKey
]);
}
foreach ($account['users'] as $user) {
$lookupUser = false;
if ($validate || $update) {
$lookupUser = LookupUser::whereLookupAccountId($lookupAccount->id)->whereUserId($user['user_id'])->first();
}
if ($validate) {
if (! $lookupUser) {
$this->logError("LookupUser - lookupAccountId: {$lookupAccount->id}, userId: {$user['user_id']} | Not found!");
continue;
} elseif ($user['email'] != $lookupUser->email || $user['oauth_user_key'] != $lookupUser->oauth_user_key || $user['referral_code'] != $lookupUser->referral_code) {
$this->logError("LookupUser - lookupAccountId: {$lookupAccount->id}, userId: {$user['user_id']} | Out of date!");
continue;
}
}
if ($update && $lookupUser) {
if ($user['email'] != $lookupUser->email || $user['oauth_user_key'] != $lookupUser->oauth_user_key || $user['referral_code'] != $lookupUser->referral_code) {
$lookupUser->email = $user['email'];
$lookupUser->oauth_user_key = $user['oauth_user_key'];
$lookupUser->referral_code = $user['referral_code'];
$lookupUser->save();
}
} elseif (! $lookupUser) {
LookupUser::create([
'lookup_account_id' => $lookupAccount->id,
'email' => $user['email'] ?: null,
'user_id' => $user['user_id'],
'oauth_user_key' => $user['oauth_user_key'],
'referral_code' => $user['referral_code'],
]);
}
}
foreach ($account['contacts'] as $contact) {
$lookupContact = false;
if ($validate || $update) {
$lookupContact = LookupContact::whereLookupAccountId($lookupAccount->id)->whereContactKey($contact['contact_key'])->first();
}
if ($validate && ! $lookupContact) {
$this->logError("LookupContact - lookupAccountId: {$lookupAccount->id}, contactKey: {$contact['contact_key']} | Not found!");
continue;
}
if (! $lookupContact) {
LookupContact::create([
'lookup_account_id' => $lookupAccount->id,
'contact_key' => $contact['contact_key'],
]);
}
}
foreach ($account['invitations'] as $invitation) {
$lookupInvitation = false;
if ($validate || $update) {
$lookupInvitation = LookupInvitation::whereLookupAccountId($lookupAccount->id)->whereInvitationKey($invitation['invitation_key'])->first();
}
if ($validate) {
if (! $lookupInvitation) {
$this->logError("LookupInvitation - lookupAccountId: {$lookupAccount->id}, invitationKey: {$invitation['invitation_key']} | Not found!");
continue;
} elseif ($invitation['message_id'] && $lookupInvitation->message_id != $invitation['message_id']) {
$this->logError("LookupInvitation - lookupAccountId: {$lookupAccount->id}, invitationKey: {$invitation['invitation_key']} | Not the same!");
continue;
}
}
if ($update && $lookupInvitation) {
if ($invitation['message_id'] && $lookupInvitation->message_id != $invitation['message_id']) {
$lookupInvitation->message_id = $invitation['message_id'];
$lookupInvitation->save();
}
} elseif (! $lookupInvitation) {
LookupInvitation::create([
'lookup_account_id' => $lookupAccount->id,
'invitation_key' => $invitation['invitation_key'],
'message_id' => $invitation['message_id'] ?: null,
]);
}
}
foreach ($account['tokens'] as $token) {
$lookupToken = false;
if ($validate || $update) {
$lookupToken = LookupAccountToken::whereLookupAccountId($lookupAccount->id)->whereToken($token['token'])->first();
}
if ($validate && ! $lookupToken) {
$this->logError("LookupAccountToken - lookupAccountId: {$lookupAccount->id}, token: {$token['token']} | Not found!");
continue;
}
if (! $lookupToken) {
LookupAccountToken::create([
'lookup_account_id' => $lookupAccount->id,
'token' => $token['token'],
]);
}
}
}
}
}
private function parseCompany($companyId)
{
$data = [];
config(['database.default' => $this->option('database')]);
$accounts = DB::table('accounts')->whereCompanyId($companyId)->orderBy('id')->get([
'id', 'account_key'
]);
foreach ($accounts as $account) {
$data[$account->account_key] = $this->parseAccount($account->id);
}
return $data;
}
private function parseAccount($accountId)
{
$data = [
'users' => [],
'contacts' => [],
'invitations' => [],
'tokens' => [],
];
$users = DB::table('users')->whereAccountId($accountId)->orderBy('id')->get([
'email',
'id',
'oauth_user_id',
'oauth_provider_id',
'referral_code',
]);
foreach ($users as $user) {
$data['users'][] = [
'email' => $user->email,
'user_id' => $user->id,
'oauth_user_key' => ($user->oauth_provider_id && $user->oauth_user_id) ? ($user->oauth_provider_id . '-' . $user->oauth_user_id) : null,
'referral_code' => $user->referral_code,
];
}
$contacts = DB::table('contacts')->whereAccountId($accountId)->orderBy('id')->get([
'contact_key'
]);
foreach ($contacts as $contact) {
$data['contacts'][] = [
'contact_key' => $contact->contact_key,
];
}
$invitations = DB::table('invitations')->whereAccountId($accountId)->orderBy('id')->get([
'invitation_key',
'message_id'
]);
foreach ($invitations as $invitation) {
$data['invitations'][] = [
'invitation_key' => $invitation->invitation_key,
'message_id' => $invitation->message_id,
];
}
$tokens = DB::table('account_tokens')->whereAccountId($accountId)->orderBy('id')->get([
'token'
]);
foreach ($tokens as $token) {
$data['tokens'][] = [
'token' => $token->token,
];
}
return $data;
}
private function logMessage($str)
{
$str = date('Y-m-d h:i:s') . ' ' . $str;
$this->info($str);
$this->log .= $str . "\n";
}
private function logError($str)
{
$this->isValid = false;
$this->logMessage($str);
}
private function truncateTables()
{
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
DB::statement('truncate lookup_companies');
DB::statement('truncate lookup_accounts');
DB::statement('truncate lookup_users');
DB::statement('truncate lookup_contacts');
DB::statement('truncate lookup_invitations');
DB::statement('truncate lookup_proposal_invitations');
DB::statement('truncate lookup_account_tokens');
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
}
protected function getOptions()
{
return [
['subdomain', null, InputOption::VALUE_OPTIONAL, 'Subdomain', null],
['truncate', null, InputOption::VALUE_OPTIONAL, 'Truncate', null],
['company_id', null, InputOption::VALUE_OPTIONAL, 'Company Id', null],
['page_size', null, InputOption::VALUE_OPTIONAL, 'Page Size', null],
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
['validate', null, InputOption::VALUE_OPTIONAL, 'Validate', null],
];
}
}

View file

@ -0,0 +1,173 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
use Nwidart\Modules\Commands\GeneratorCommand;
use Nwidart\Modules\Support\Stub;
use Nwidart\Modules\Traits\ModuleCommandTrait;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
class MakeClass extends GeneratorCommand
{
use ModuleCommandTrait;
protected $argumentName = 'name';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $name = 'ninja:make-class';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create class stub';
protected function getArguments()
{
return [
['name', InputArgument::REQUIRED, 'The name of the module.'],
['module', InputArgument::REQUIRED, 'The name of module will be used.'],
['class', InputArgument::REQUIRED, 'The name of the class.'],
['prefix', InputArgument::OPTIONAL, 'The prefix of the class.'],
];
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['fields', null, InputOption::VALUE_OPTIONAL, 'The model attributes.', null],
['filename', null, InputOption::VALUE_OPTIONAL, 'The class filename.', null],
];
}
public function getTemplateContents()
{
$module = $this->laravel['modules']->findOrFail($this->getModuleName());
$path = str_replace('/', '\\', config('modules.paths.generator.' . $this->argument('class')));
return (new Stub('/' . $this->argument('prefix') . $this->argument('class') . '.stub', [
'NAMESPACE' => $this->getClassNamespace($module) . '\\' . $path,
'LOWER_NAME' => $module->getLowerName(),
'CLASS' => $this->getClass(),
'STUDLY_NAME' => Str::studly($module->getLowerName()),
'DATATABLE_COLUMNS' => $this->getColumns(),
'FORM_FIELDS' => $this->getFormFields(),
'DATABASE_FIELDS' => $this->getDatabaseFields($module),
'TRANSFORMER_FIELDS' => $this->getTransformerFields($module),
]))->render();
}
public function getDestinationFilePath()
{
$path = $this->laravel['modules']->getModulePath($this->getModuleName());
$seederPath = $this->laravel['modules']->config('paths.generator.' . $this->argument('class'));
return $path . $seederPath . '/' . $this->getFileName() . '.php';
}
/**
* @return string
*/
protected function getFileName()
{
if ($this->option('filename')) {
return $this->option('filename');
}
return studly_case($this->argument('prefix')) . studly_case($this->argument('name')) . Str::studly($this->argument('class'));
}
protected function getColumns()
{
$fields = $this->option('fields');
$fields = explode(',', $fields);
$str = '';
foreach ($fields as $field) {
if (! $field) {
continue;
}
$field = explode(':', $field)[0];
$str .= '[
\''. $field . '\',
function ($model) {
return $model->' . $field . ';
}
],';
}
return $str;
}
protected function getFormFields()
{
$fields = $this->option('fields');
$fields = explode(',', $fields);
$str = '';
foreach ($fields as $field) {
if (! $field) {
continue;
}
$parts = explode(':', $field);
$field = $parts[0];
$type = $parts[1];
if ($type == 'text') {
$str .= "{!! Former::textarea('" . $field . "') !!}\n";
} else {
$str .= "{!! Former::text('" . $field . "') !!}\n";
}
}
return $str;
}
protected function getDatabaseFields($module)
{
$fields = $this->option('fields');
$fields = explode(',', $fields);
$str = '';
foreach ($fields as $field) {
if (! $field) {
continue;
}
$field = explode(':', $field)[0];
$str .= "'" . $module->getLowerName() . ".{$field}', ";
}
return $str;
}
protected function getTransformerFields($module)
{
$fields = $this->option('fields');
$fields = explode(',', $fields);
$str = '';
foreach ($fields as $field) {
if (! $field) {
continue;
}
$field = explode(':', $field)[0];
$str .= "'{$field}' => $" . $module->getLowerName() . "->$field,\n ";
}
return rtrim($str);
}
}

View file

@ -0,0 +1,151 @@
<?php
namespace App\Console\Commands;
use Artisan;
use Illuminate\Console\Command;
use Symfony\Component\Console\Helper\ProgressBar;
class MakeModule extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:make-module {name : Module name} {fields? : Model fields} {--migrate : Run module migrations} {--p|--plain : Generate only base module scaffold}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate Module CRUD';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$name = $this->argument('name');
$fields = $this->argument('fields');
$migrate = $this->option('migrate');
$plain = $this->option('plain');
$lower = strtolower($name);
// convert 'name:string,description:text' to 'name,description'
$fillable = explode(',', $fields);
$fillable = array_map(function ($item) {
return explode(':', $item)[0];
}, $fillable);
$fillable = implode(',', $fillable);
ProgressBar::setFormatDefinition('custom', '%current%/%max% %elapsed:6s% [%bar%] %percent:3s%% %message%');
$progressBar = $this->output->createProgressBar($plain ? 2 : ($migrate ? 15 : 14));
$progressBar->setFormat('custom');
$this->info("Creating module: {$name}...");
$progressBar->setMessage("Starting module creation...");
Artisan::call('module:make', ['name' => [$name]]);
$progressBar->advance();
if (! $plain) {
$progressBar->setMessage("Creating migrations...");
Artisan::call('module:make-migration', ['name' => "create_{$lower}_table", '--fields' => $fields, 'module' => $name]);
$progressBar->advance();
$progressBar->setMessage("Creating models...");
Artisan::call('module:make-model', ['model' => $name, 'module' => $name, '--fillable' => $fillable]);
$progressBar->advance();
$progressBar->setMessage("Creating views...");
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'views', '--fields' => $fields, '--filename' => 'edit.blade']);
$progressBar->advance();
$progressBar->setMessage("Creating datatables...");
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'datatable', '--fields' => $fields]);
$progressBar->advance();
$progressBar->setMessage("Creating repositories...");
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'repository', '--fields' => $fields]);
$progressBar->advance();
$progressBar->setMessage("Creating presenters...");
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'presenter']);
$progressBar->advance();
$progressBar->setMessage("Creating requests...");
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'request']);
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'request', 'prefix' => 'create']);
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'request', 'prefix' => 'update']);
$progressBar->advance();
$progressBar->setMessage("Creating api-controllers...");
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'api-controller']);
$progressBar->advance();
$progressBar->setMessage("Creating transformers...");
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'transformer', '--fields' => $fields]);
$progressBar->advance();
// if the migrate flag was specified, run the migrations
if ($migrate) {
$progressBar->setMessage("Running migrations...");
Artisan::call('module:migrate', ['module' => $name]);
$progressBar->advance();
}
}
$progressBar->setMessage("Creating policies...");
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'policy']);
$progressBar->advance();
$progressBar->setMessage("Creating auth-providers...");
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'auth-provider']);
$progressBar->advance();
$progressBar->setMessage("Creating translations...");
Artisan::call('ninja:make-class', ['name' => $name, 'module' => $name, 'class' => 'lang', '--filename' => 'texts']);
$progressBar->advance();
$progressBar->setMessage("Dumping module auto-load...");
Artisan::call('module:dump');
$progressBar->finish();
$progressBar->clear();
$this->info('Done');
if (!$migrate && !$plain) {
$this->info("==> Migrations were not run because the --migrate flag was not specified.");
$this->info("==> Use the following command to run the migrations:\nphp artisan module:migrate $name");
}
}
protected function getArguments()
{
return [
['name', InputArgument::REQUIRED, 'The name of the module.'],
['fields', InputArgument::OPTIONAL, 'The fields of the module.'],
];
}
protected function getOptions()
{
return [
['migrate', null, InputOption::VALUE_NONE, 'Run module migrations.', null],
['plain', 'p', InputOption::VALUE_NONE, 'Generate only base module scaffold.', null],
];
}
}

View file

@ -2,10 +2,11 @@
namespace App\Console\Commands;
use App\Models\Company;
use App\Models\User;
use App\Utils\CurlUtils;
use Illuminate\Console\Command;
use App\Models\DbServer;
use App\Models\User;
use App\Models\Company;
use App\Libraries\CurlUtils;
class MobileLocalization extends Command
{

View file

@ -1,66 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Console\Commands;
use App;
use App\Jobs\Ninja\CheckCompanyData;
use App\Models\Account;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Company;
use App\Models\CompanyLedger;
use App\Models\Contact;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\InvoiceInvitation;
use App\Models\Payment;
use App\Utils\Ninja;
use DB;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
use Mail;
use Symfony\Component\Console\Input\InputOption;
/**
* Class CheckData.
*/
class ParallelCheckData extends Command
{
/**
* @var string
*/
protected $name = 'ninja:pcheck-data';
/**
* @var string
*/
protected $description = 'Check company data in parallel';
protected $log = '';
protected $isValid = true;
public function handle()
{
$hash = Str::random(32);
Company::cursor()->each(function ($company) use ($hash){
CheckCompanyData::dispatch($company, $hash)->onQueue('checkdata');
});
}
}

View file

@ -1,83 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Console\Commands;
use App\Jobs\Util\VersionCheck;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
class PostUpdate extends Command
{
protected $name = 'ninja:post-update';
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:post-update';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Run basic upgrade commands';
/**
* Execute the console command.
*
* @return mixed
* @throws \Exception
*/
public function handle()
{
set_time_limit(0);
info('running post update');
try {
Artisan::call('migrate', ['--force' => true]);
} catch (\Exception $e) {
info("I wasn't able to migrate the data.");
}
nlog("finished migrating");
$output = [];
exec('vendor/bin/composer install --no-dev -o', $output);
info(print_r($output,1));
info("finished running composer install ");
try {
Artisan::call('optimize');
} catch (\Exception $e) {
info("I wasn't able to optimize.");
}
info("optimized");
try {
Artisan::call('view:clear');
} catch (\Exception $e) {
info("I wasn't able to clear the views.");
}
info("view cleared");
VersionCheck::dispatch();
info("Sent for version check");
}
}

View file

@ -0,0 +1,84 @@
<?php
namespace App\Console\Commands;
use DB;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
/**
* Class PruneData.
*/
class PruneData extends Command
{
/**
* @var string
*/
protected $name = 'ninja:prune-data';
/**
* @var string
*/
protected $description = 'Delete inactive accounts';
public function handle()
{
$this->info(date('r').' Running PruneData...');
if ($database = $this->option('database')) {
config(['database.default' => $database]);
}
// delete accounts who never registered, didn't create any invoices,
// hansn't logged in within the past 6 months and isn't linked to another account
$sql = 'select c.id
from companies c
left join accounts a on a.company_id = c.id
left join clients cl on cl.account_id = a.id
left join tasks t on t.account_id = a.id
left join expenses e on e.account_id = a.id
left join users u on u.account_id = a.id and u.registered = 1
where c.created_at < DATE_SUB(now(), INTERVAL 6 MONTH)
and c.trial_started is null
and c.plan is null
group by c.id
having count(cl.id) = 0
and count(t.id) = 0
and count(e.id) = 0
and count(u.id) = 0';
$results = DB::select($sql);
foreach ($results as $result) {
$this->info("Deleting company: {$result->id}");
try {
DB::table('companies')
->where('id', '=', $result->id)
->delete();
} catch (\Illuminate\Database\QueryException $e) {
// most likely because a user_account record exists which doesn't cascade delete
$this->info("Unable to delete companyId: {$result->id}");
}
}
$this->info('Done');
}
/**
* @return array
*/
protected function getArguments()
{
return [];
}
/**
* @return array
*/
protected function getOptions()
{
return [
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
];
}
}

View file

@ -1,52 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Console\Commands;
use App\Jobs\Cron\RecurringInvoicesCron;
use Illuminate\Console\Command;
class RecurringCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:send-recurring';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sends the recurring invoices';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
RecurringInvoicesCron::dispatchNow();
}
}

View file

@ -0,0 +1,61 @@
<?php
namespace App\Console\Commands;
use App\Models\Document;
use DateTime;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
/**
* Class RemoveOrphanedDocuments.
*/
class RemoveOrphanedDocuments extends Command
{
/**
* @var string
*/
protected $name = 'ninja:remove-orphaned-documents';
/**
* @var string
*/
protected $description = 'Removes old documents not associated with an expense or invoice';
public function handle()
{
$this->info(date('r').' Running RemoveOrphanedDocuments...');
if ($database = $this->option('database')) {
config(['database.default' => $database]);
}
$documents = Document::whereRaw('invoice_id IS NULL AND expense_id IS NULL AND updated_at <= ?', [new DateTime('-1 hour')])
->get();
$this->info($documents->count() . ' orphaned document(s) found');
foreach ($documents as $document) {
$document->delete();
}
$this->info('Done');
}
/**
* @return array
*/
protected function getArguments()
{
return [];
}
/**
* @return array
*/
protected function getOptions()
{
return [
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
];
}
}

View file

@ -0,0 +1,52 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Utils;
use Symfony\Component\Console\Input\InputOption;
/**
* Class ResetData.
*/
class ResetData extends Command
{
/**
* @var string
*/
protected $name = 'ninja:reset-data';
/**
* @var string
*/
protected $description = 'Reset data';
public function handle()
{
$this->info(date('r') . ' Running ResetData...');
if (! Utils::isNinjaDev()) {
return;
}
if ($database = $this->option('database')) {
config(['database.default' => $database]);
}
Artisan::call('migrate:reset');
Artisan::call('migrate');
Artisan::call('db:seed');
}
/**
* @return array
*/
protected function getOptions()
{
return [
['fix', null, InputOption::VALUE_OPTIONAL, 'Fix data', null],
['client_id', null, InputOption::VALUE_OPTIONAL, 'Client id', null],
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
];
}
}

View file

@ -1,71 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\Company;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
class S3Cleanup extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:s3-cleanup';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Remove orphan folders';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$c1 = Company::on('db-ninja-01')->pluck('company_key');
$c2 = Company::on('db-ninja-02')->pluck('company_key');
$merged = $c1->merge($c2)->toArray();
$directories = Storage::disk(config('filesystems.default'))->directories();
$this->LogMessage("Disk Cleanup");
foreach($directories as $dir)
{
if(!in_array($dir, $merged))
{
$this->logMessage("Deleting $dir");
Storage::disk(config('filesystems.default'))->deleteDirectory($dir);
}
}
$this->logMessage("exiting");
}
private function logMessage($str)
{
$str = date('Y-m-d h:i:s').' '.$str;
$this->info($str);
$this->log .= $str."\n";
}
}

View file

@ -0,0 +1,156 @@
<?php
namespace App\Console\Commands;
use App\Models\Account;
use App\Models\Invoice;
use App\Models\RecurringExpense;
use App\Ninja\Repositories\InvoiceRepository;
use App\Ninja\Repositories\RecurringExpenseRepository;
use App\Jobs\SendInvoiceEmail;
use DateTime;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Auth;
use Exception;
use Utils;
/**
* Class SendRecurringInvoices.
*/
class SendRecurringInvoices extends Command
{
/**
* @var string
*/
protected $name = 'ninja:send-invoices';
/**
* @var string
*/
protected $description = 'Send recurring invoices';
/**
* @var InvoiceRepository
*/
protected $invoiceRepo;
/**
* SendRecurringInvoices constructor.
*
* @param InvoiceRepository $invoiceRepo
*/
public function __construct(InvoiceRepository $invoiceRepo, RecurringExpenseRepository $recurringExpenseRepo)
{
parent::__construct();
$this->invoiceRepo = $invoiceRepo;
$this->recurringExpenseRepo = $recurringExpenseRepo;
}
public function handle()
{
$this->info(date('r') . ' Running SendRecurringInvoices...');
if ($database = $this->option('database')) {
config(['database.default' => $database]);
}
$this->resetCounters();
$this->createInvoices();
$this->createExpenses();
$this->info(date('r') . ' Done');
}
private function resetCounters()
{
$accounts = Account::where('reset_counter_frequency_id', '>', 0)
->orderBy('id', 'asc')
->get();
foreach ($accounts as $account) {
$account->checkCounterReset();
}
}
private function createInvoices()
{
$today = new DateTime();
$invoices = Invoice::with('account.timezone', 'invoice_items', 'client', 'user')
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS TRUE AND is_public IS TRUE AND frequency_id > 0 AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', [$today, $today])
->orderBy('id', 'asc')
->cursor();
$this->info(date('r ') . ' Recurring invoice(s) found');
foreach ($invoices as $recurInvoice) {
$shouldSendToday = $recurInvoice->shouldSendToday();
if (! $shouldSendToday) {
continue;
}
$this->info(date('r') . ' Processing Invoice: '. $recurInvoice->id);
$account = $recurInvoice->account;
$account->loadLocalizationSettings($recurInvoice->client);
Auth::loginUsingId($recurInvoice->activeUser()->id);
try {
$invoice = $this->invoiceRepo->createRecurringInvoice($recurInvoice);
if ($invoice && ! $invoice->isPaid() && $account->auto_email_invoice) {
$this->info(date('r') . ' Not billed - Sending Invoice');
dispatch(new SendInvoiceEmail($invoice, $invoice->user_id));
} elseif ($invoice) {
$this->info(date('r') . ' Successfully billed invoice');
}
} catch (Exception $exception) {
$this->info(date('r') . ' Error: ' . $exception->getMessage());
Utils::logError($exception);
}
Auth::logout();
}
}
private function createExpenses()
{
$today = new DateTime();
$expenses = RecurringExpense::with('client')
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)', [$today, $today])
->orderBy('id', 'asc')
->get();
$this->info(date('r ') . $expenses->count() . ' recurring expenses(s) found');
foreach ($expenses as $expense) {
$shouldSendToday = $expense->shouldSendToday();
if (! $shouldSendToday) {
continue;
}
$this->info(date('r') . ' Processing Expense: '. $expense->id);
$this->recurringExpenseRepo->createRecurringExpense($expense);
}
}
/**
* @return array
*/
protected function getArguments()
{
return [];
}
/**
* @return array
*/
protected function getOptions()
{
return [
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
];
}
}

View file

@ -0,0 +1,280 @@
<?php
namespace App\Console\Commands;
use App\Libraries\CurlUtils;
use Carbon;
use Str;
use Cache;
use Utils;
use Exception;
use DateTime;
use Auth;
use App\Jobs\SendInvoiceEmail;
use App\Models\Invoice;
use App\Models\Currency;
use App\Ninja\Mailers\UserMailer;
use App\Ninja\Repositories\AccountRepository;
use App\Ninja\Repositories\InvoiceRepository;
use App\Models\ScheduledReport;
use App\Services\PaymentService;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use App\Jobs\ExportReportResults;
use App\Jobs\RunReport;
/**
* Class SendReminders.
*/
class SendReminders extends Command
{
/**
* @var string
*/
protected $name = 'ninja:send-reminders';
/**
* @var string
*/
protected $description = 'Send reminder emails';
/**
* @var InvoiceRepository
*/
protected $invoiceRepo;
/**
* @var accountRepository
*/
protected $accountRepo;
/**
* @var PaymentService
*/
protected $paymentService;
/**
* SendReminders constructor.
*
* @param Mailer $mailer
* @param InvoiceRepository $invoiceRepo
* @param accountRepository $accountRepo
*/
public function __construct(InvoiceRepository $invoiceRepo, PaymentService $paymentService, AccountRepository $accountRepo, UserMailer $userMailer)
{
parent::__construct();
$this->paymentService = $paymentService;
$this->invoiceRepo = $invoiceRepo;
$this->accountRepo = $accountRepo;
$this->userMailer = $userMailer;
}
public function handle()
{
$this->info(date('r') . ' Running SendReminders...');
if ($database = $this->option('database')) {
config(['database.default' => $database]);
}
$this->billInvoices();
$this->chargeLateFees();
$this->sendReminderEmails();
$this->sendScheduledReports();
$this->loadExchangeRates();
$this->info(date('r') . ' Done');
if ($errorEmail = env('ERROR_EMAIL')) {
\Mail::raw('EOM', function ($message) use ($errorEmail, $database) {
$message->to($errorEmail)
->from(CONTACT_EMAIL)
->subject("SendReminders [{$database}]: Finished successfully");
});
}
}
private function billInvoices()
{
$today = new DateTime();
$delayedAutoBillInvoices = Invoice::with('account.timezone', 'recurring_invoice', 'invoice_items', 'client', 'user')
->whereRaw('is_deleted IS FALSE AND deleted_at IS NULL AND is_recurring IS FALSE AND is_public IS TRUE
AND balance > 0 AND due_date = ? AND recurring_invoice_id IS NOT NULL',
[$today->format('Y-m-d')])
->orderBy('invoices.id', 'asc')
->get();
$this->info(date('r ') . $delayedAutoBillInvoices->count() . ' due recurring invoice instance(s) found');
/** @var Invoice $invoice */
foreach ($delayedAutoBillInvoices as $invoice) {
if ($invoice->isPaid()) {
continue;
}
if ($invoice->getAutoBillEnabled() && $invoice->client->autoBillLater()) {
$this->info(date('r') . ' Processing Autobill-delayed Invoice: ' . $invoice->id);
Auth::loginUsingId($invoice->activeUser()->id);
$this->paymentService->autoBillInvoice($invoice);
Auth::logout();
}
}
}
private function chargeLateFees()
{
$accounts = $this->accountRepo->findWithFees();
$this->info(date('r ') . $accounts->count() . ' accounts found with fees enabled');
foreach ($accounts as $account) {
if (! $account->hasFeature(FEATURE_EMAIL_TEMPLATES_REMINDERS)) {
continue;
}
$invoices = $this->invoiceRepo->findNeedingReminding($account, false);
$this->info(date('r ') . $account->name . ': ' . $invoices->count() . ' invoices found');
foreach ($invoices as $invoice) {
if ($reminder = $account->getInvoiceReminder($invoice, false)) {
$this->info(date('r') . ' Charge fee: ' . $invoice->id);
$account->loadLocalizationSettings($invoice->client); // support trans to add fee line item
$number = preg_replace('/[^0-9]/', '', $reminder);
$amount = $account->account_email_settings->{"late_fee{$number}_amount"};
$percent = $account->account_email_settings->{"late_fee{$number}_percent"};
$this->invoiceRepo->setLateFee($invoice, $amount, $percent);
}
}
}
}
private function sendReminderEmails()
{
$accounts = $this->accountRepo->findWithReminders();
$this->info(date('r ') . count($accounts) . ' accounts found with reminders enabled');
foreach ($accounts as $account) {
if (! $account->hasFeature(FEATURE_EMAIL_TEMPLATES_REMINDERS)) {
continue;
}
// standard reminders
$invoices = $this->invoiceRepo->findNeedingReminding($account);
$this->info(date('r ') . $account->name . ': ' . $invoices->count() . ' invoices found');
foreach ($invoices as $invoice) {
if ($reminder = $account->getInvoiceReminder($invoice)) {
if ($invoice->last_sent_date == date('Y-m-d')) {
continue;
}
$this->info(date('r') . ' Send email: ' . $invoice->id);
dispatch(new SendInvoiceEmail($invoice, $invoice->user_id, $reminder));
}
}
// endless reminders
$invoices = $this->invoiceRepo->findNeedingEndlessReminding($account);
$this->info(date('r ') . $account->name . ': ' . $invoices->count() . ' endless invoices found');
foreach ($invoices as $invoice) {
if ($invoice->last_sent_date == date('Y-m-d')) {
continue;
}
$this->info(date('r') . ' Send email: ' . $invoice->id);
dispatch(new SendInvoiceEmail($invoice, $invoice->user_id, 'reminder4'));
}
}
}
private function sendScheduledReports()
{
$scheduledReports = ScheduledReport::where('send_date', '<=', date('Y-m-d'))
->with('user', 'account.company')
->get();
$this->info(date('r ') . $scheduledReports->count() . ' scheduled reports');
foreach ($scheduledReports as $scheduledReport) {
$this->info(date('r') . ' Processing report: ' . $scheduledReport->id);
$user = $scheduledReport->user;
$account = $scheduledReport->account;
$account->loadLocalizationSettings();
if (! $account->hasFeature(FEATURE_REPORTS)) {
continue;
}
$config = (array) json_decode($scheduledReport->config);
$reportType = $config['report_type'];
// send email as user
auth()->onceUsingId($user->id);
$report = dispatch_now(new RunReport($scheduledReport->user, $reportType, $config, true));
$file = dispatch_now(new ExportReportResults($scheduledReport->user, $config['export_format'], $reportType, $report->exportParams));
if ($file) {
try {
$this->userMailer->sendScheduledReport($scheduledReport, $file);
$this->info(date('r') . ' Sent report');
} catch (Exception $exception) {
$this->info(date('r') . ' ERROR: ' . $exception->getMessage());
}
} else {
$this->info(date('r') . ' ERROR: Failed to run report');
}
$scheduledReport->updateSendDate();
auth()->logout();
}
}
private function loadExchangeRates()
{
if (Utils::isNinjaDev()) {
return;
}
if (config('ninja.exchange_rates_enabled')) {
$this->info(date('r') . ' Loading latest exchange rates...');
$response = CurlUtils::get(config('ninja.exchange_rates_url'));
$data = json_decode($response);
if ($data && property_exists($data, 'rates')) {
Currency::whereCode(config('ninja.exchange_rates_base'))->update(['exchange_rate' => 1]);
foreach ($data->rates as $code => $rate) {
Currency::whereCode($code)->update(['exchange_rate' => $rate]);
}
} else {
$this->info(date('r') . ' Error: failed to load exchange rates - ' . $response);
\DB::table('currencies')->update(['exchange_rate' => 1]);
}
} else {
\DB::table('currencies')->update(['exchange_rate' => 1]);
}
CurlUtils::get(SITE_URL . '?clear_cache=true');
}
/**
* @return array
*/
protected function getArguments()
{
return [];
}
/**
* @return array
*/
protected function getOptions()
{
return [
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
];
}
}

View file

@ -1,107 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Console\Commands;
use App\Jobs\Ninja\SendReminders;
use App\Libraries\MultiDB;
use App\Models\Invoice;
use App\Models\Quote;
use App\Models\Webhook;
use Illuminate\Console\Command;
use App\Jobs\Util\WebhookHandler;
class SendRemindersCron extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:send-reminders';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Force send all reminders';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
SendReminders::dispatchNow();
$this->webHookOverdueInvoices();
$this->webHookExpiredQuotes();
}
private function webHookOverdueInvoices()
{
if (! config('ninja.db.multi_db_enabled')) {
$this->executeWebhooks();
} else {
//multiDB environment, need to
foreach (MultiDB::$dbs as $db) {
MultiDB::setDB($db);
$this->executeWebhooks();
}
}
}
private function webHookExpiredQuotes()
{
}
private function executeWebhooks()
{
$invoices = Invoice::where('is_deleted', 0)
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
->where('balance', '>', 0)
->whereDate('due_date', '<=', now()->subDays(1)->startOfDay())
->cursor();
$invoices->each(function ($invoice) {
WebhookHandler::dispatch(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company);
});
$quotes = Quote::where('is_deleted', 0)
->where('status_id', Quote::STATUS_SENT)
->whereDate('due_date', '<=', now()->subDays(1)->startOfDay())
->cursor();
$quotes->each(function ($quote) {
WebhookHandler::dispatch(Webhook::EVENT_EXPIRED_QUOTE, $quote, $quote->company);
});
}
}

View file

@ -0,0 +1,128 @@
<?php
namespace App\Console\Commands;
use App\Models\Company;
use App\Ninja\Mailers\ContactMailer as Mailer;
use App\Ninja\Repositories\AccountRepository;
use Illuminate\Console\Command;
use Utils;
use Symfony\Component\Console\Input\InputOption;
/**
* Class SendRenewalInvoices.
*/
class SendRenewalInvoices extends Command
{
/**
* @var string
*/
protected $name = 'ninja:send-renewals';
/**
* @var string
*/
protected $description = 'Send renewal invoices';
/**
* @var Mailer
*/
protected $mailer;
/**
* @var AccountRepository
*/
protected $accountRepo;
/**
* SendRenewalInvoices constructor.
*
* @param Mailer $mailer
* @param AccountRepository $repo
*/
public function __construct(Mailer $mailer, AccountRepository $repo)
{
parent::__construct();
$this->mailer = $mailer;
$this->accountRepo = $repo;
}
public function handle()
{
$this->info(date('r').' Running SendRenewalInvoices...');
if ($database = $this->option('database')) {
config(['database.default' => $database]);
}
// get all accounts with plans expiring in 10 days
$companies = Company::whereRaw("datediff(plan_expires, curdate()) = 10 and (plan = 'pro' or plan = 'enterprise')")
->orderBy('id')
->get();
$this->info($companies->count() . ' companies found renewing in 10 days');
foreach ($companies as $company) {
if (! $company->accounts->count()) {
continue;
}
$account = $company->accounts->sortBy('id')->first();
$plan = [];
$plan['plan'] = $company->plan;
$plan['term'] = $company->plan_term;
$plan['num_users'] = $company->num_users;
$plan['price'] = min($company->plan_price, Utils::getPlanPrice($plan));
if ($plan['plan'] == PLAN_FREE || ! $plan['plan'] || ! $plan['term'] || ! $plan['price']) {
continue;
}
$client = $this->accountRepo->getNinjaClient($account);
$invitation = $this->accountRepo->createNinjaInvoice($client, $account, $plan, 0, false);
// set the due date to 10 days from now
$invoice = $invitation->invoice;
$invoice->due_date = date('Y-m-d', strtotime('+ 10 days'));
$invoice->save();
$term = $plan['term'];
$plan = $plan['plan'];
if ($term == PLAN_TERM_YEARLY) {
$this->mailer->sendInvoice($invoice);
$this->info("Sent {$term}ly {$plan} invoice to {$client->getDisplayName()}");
} else {
$this->info("Created {$term}ly {$plan} invoice for {$client->getDisplayName()}");
}
}
$this->info('Done');
if ($errorEmail = env('ERROR_EMAIL')) {
\Mail::raw('EOM', function ($message) use ($errorEmail, $database) {
$message->to($errorEmail)
->from(CONTACT_EMAIL)
->subject("SendRenewalInvoices [{$database}]: Finished successfully");
});
}
}
/**
* @return array
*/
protected function getArguments()
{
return [];
}
/**
* @return array
*/
protected function getOptions()
{
return [
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
];
}
}

View file

@ -1,101 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Console\Commands;
use App\DataMapper\CompanySettings;
use App\DataMapper\DefaultSettings;
use App\Factory\ClientFactory;
use App\Factory\InvoiceFactory;
use App\Factory\InvoiceInvitationFactory;
use App\Jobs\Invoice\CreateEntityPdf;
use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Mail\Migration\MaxCompanies;
use App\Mail\TemplateEmail;
use App\Models\Account;
use App\Models\Client;
use App\Models\ClientContact;
use App\Models\Company;
use App\Models\User;
use Faker\Factory;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
class SendTestEmails extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:send-test-emails';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sends Test Emails to check templates';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$faker = Factory::create();
$account = Account::factory()->create();
$user = User::factory()->create([
'account_id' => $account->id,
'confirmation_code' => '123',
'email' => $faker->safeEmail,
'first_name' => 'John',
'last_name' => 'Doe',
]);
$company = Company::factory()->create([
'account_id' => $account->id,
]);
$user->companies()->attach($company->id, [
'account_id' => $account->id,
'is_owner' => 1,
'is_admin' => 1,
'is_locked' => 0,
'permissions' => '',
'notifications' => CompanySettings::notificationDefaults(),
//'settings' => DefaultSettings::userSettings(),
'settings' => null,
]);
$nmo = new NinjaMailerObject;
$nmo->mailable = new MaxCompanies($user->account->companies()->first());
$nmo->company = $user->account->companies()->first();
$nmo->settings = $user->account->companies()->first()->settings;
$nmo->to_user = $user;
NinjaMailerJob::dispatchNow($nmo);
}
}

View file

@ -1,95 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Libraries\MultiDB;
use App\Models\Company;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
class SubdomainFill extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'ninja:subdomain';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Pad subdomains';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$c1 = Company::on('db-ninja-01')->whereNull('subdomain')->orWhere('subdomain', '')->get();
$c2 = Company::on('db-ninja-02')->whereNull('subdomain')->orWhere('subdomain', '')->get();
$c1->each(function ($company){
$company->subdomain = MultiDB::randomSubdomainGenerator();
$company->save();
});
$c2->each(function ($company){
$company->subdomain = MultiDB::randomSubdomainGenerator();
$company->save();
});
// $db1 = Company::on('db-ninja-01')->get();
// $db1->each(function ($company){
// $db2 = Company::on('db-ninja-02a')->find($company->id);
// if($db2)
// {
// $db2->subdomain = $company->subdomain;
// $db2->save();
// }
// });
// $db1 = null;
// $db2 = null;
// $db2 = Company::on('db-ninja-02')->get();
// $db2->each(function ($company){
// $db1 = Company::on('db-ninja-01a')->find($company->id);
// if($db1)
// {
// $db1->subdomain = $company->subdomain;
// $db1->save();
// }
// });
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace App\Console\Commands;
use App\Services\BankAccountService;
use Illuminate\Console\Command;
/**
* Class TestOFX.
*/
class TestOFX extends Command
{
/**
* @var string
*/
protected $name = 'ninja:test-ofx';
/**
* @var string
*/
protected $description = 'Test OFX';
/**
* @var BankAccountService
*/
protected $bankAccountService;
/**
* TestOFX constructor.
*
* @param BankAccountService $bankAccountService
*/
public function __construct(BankAccountService $bankAccountService)
{
parent::__construct();
$this->bankAccountService = $bankAccountService;
}
public function handle()
{
$this->info(date('r').' Running TestOFX...');
}
}

View file

@ -0,0 +1,148 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use App\Models\AccountGateway;
use App\Models\BankAccount;
use App\Models\User;
use Artisan;
use Crypt;
use Illuminate\Encryption\Encrypter;
use Laravel\LegacyEncrypter\McryptEncrypter;
/**
* Class UpdateKey
*/
class UpdateKey extends Command
{
/**
* @var string
*/
protected $name = 'ninja:update-key {--database=} {--key=} {--legacy=}';
/**
* @var string
*/
protected $description = 'Update application key';
public function handle()
{
$this->info(date('r') . ' Running UpdateKey...');
if ($database = $this->option('database')) {
config(['database.default' => $database]);
}
if (! env('APP_KEY') || ! env('APP_CIPHER')) {
$this->info(date('r') . ' Error: app key and cipher are not set');
exit;
}
$legacy = false;
if ($this->option('legacy') == 'true') {
$legacy = new McryptEncrypter(env('APP_KEY'), env('APP_CIPHER'));
}
// load the current values
$gatewayConfigs = [];
$bankUsernames = [];
$twoFactorSecrets = [];
foreach (AccountGateway::withTrashed()->get() as $gateway) {
if ($legacy) {
$gatewayConfigs[$gateway->id] = json_decode($legacy->decrypt($gateway->config));
} else {
$gatewayConfigs[$gateway->id] = $gateway->getConfig();
}
}
foreach (BankAccount::withTrashed()->get() as $bank) {
if ($legacy) {
$bankUsernames[$bank->id] = $legacy->decrypt($bank->username);
} else {
$bankUsernames[$bank->id] = $bank->getUsername();
}
}
foreach (User::withTrashed()->where('google_2fa_secret', '!=', '')->get() as $user) {
if ($legacy) {
$twoFactorSecrets[$user->id] = $legacy->decrypt($user->google_2fa_secret);
} else {
$twoFactorSecrets[$user->id] = Crypt::decrypt($user->google_2fa_secret);
}
}
// check if we can write to the .env file
$envPath = base_path() . '/.env';
$envWriteable = file_exists($envPath) && @fopen($envPath, 'a');
if ($key = $this->option('key')) {
$key = base64_decode(str_replace('base64:', '', $key));
} elseif ($envWriteable) {
Artisan::call('key:generate');
$key = base64_decode(str_replace('base64:', '', config('app.key')));
} else {
$key = str_random(32);
}
$cipher = $legacy ? 'AES-256-CBC' : config('app.cipher');
$crypt = new Encrypter($key, $cipher);
// update values using the new key/encrypter
foreach (AccountGateway::withTrashed()->get() as $gateway) {
$config = $gatewayConfigs[$gateway->id];
$gateway->config = $crypt->encrypt(json_encode($config));
$gateway->save();
}
foreach (BankAccount::withTrashed()->get() as $bank) {
$username = $bankUsernames[$bank->id];
$bank->username = $crypt->encrypt($username);
$bank->save();
}
foreach (User::withTrashed()->where('google_2fa_secret', '!=', '')->get() as $user) {
$secret = $twoFactorSecrets[$user->id];
$user->google_2fa_secret = $crypt->encrypt($secret);
$user->save();
}
$message = date('r') . ' Successfully updated ';
if ($envWriteable) {
if ($legacy) {
$message .= 'the key, set the cipher in the .env file to AES-256-CBC';
} else {
$message .= 'the key';
}
} else {
if ($legacy) {
$message .= 'the data, make sure to set the new cipher/key: AES-256-CBC/' . $key;
} else {
$message .= 'the data, make sure to set the new key: ' . $key;
}
}
$this->info($message);
}
/**
* @return array
*/
protected function getArguments()
{
return [];
}
/**
* @return array
*/
protected function getOptions()
{
return [
['legacy', null, InputOption::VALUE_OPTIONAL, 'Legacy', null],
['database', null, InputOption::VALUE_OPTIONAL, 'Database', null],
['key', null, InputOption::VALUE_OPTIONAL, 'Key', null],
];
}
}

View file

@ -0,0 +1,179 @@
<?php
namespace $NAMESPACE$;
use App\Http\Controllers\BaseAPIController;
use Modules\$STUDLY_NAME$\Repositories\$STUDLY_NAME$Repository;
use Modules\$STUDLY_NAME$\Http\Requests\$STUDLY_NAME$Request;
use Modules\$STUDLY_NAME$\Http\Requests\Create$STUDLY_NAME$Request;
use Modules\$STUDLY_NAME$\Http\Requests\Update$STUDLY_NAME$Request;
class $STUDLY_NAME$ApiController extends BaseAPIController
{
protected $$STUDLY_NAME$Repo;
protected $entityType = '$LOWER_NAME$';
public function __construct($STUDLY_NAME$Repository $$LOWER_NAME$Repo)
{
parent::__construct();
$this->$LOWER_NAME$Repo = $$LOWER_NAME$Repo;
}
/**
* @SWG\Get(
* path="/$LOWER_NAME$",
* summary="List $LOWER_NAME$",
* operationId="list$STUDLY_NAME$s",
* tags={"$LOWER_NAME$"},
* @SWG\Response(
* response=200,
* description="A list of $LOWER_NAME$",
* @SWG\Schema(type="array", @SWG\Items(ref="#/definitions/$STUDLY_NAME$"))
* ),
* @SWG\Response(
* response="default",
* description="an ""unexpected"" error"
* )
* )
*/
public function index()
{
$data = $this->$LOWER_NAME$Repo->all();
return $this->listResponse($data);
}
/**
* @SWG\Get(
* path="/$LOWER_NAME$/{$LOWER_NAME$_id}",
* summary="Individual $STUDLY_NAME$",
* operationId="get$STUDLY_NAME$",
* tags={"$LOWER_NAME$"},
* @SWG\Parameter(
* in="path",
* name="$LOWER_NAME$_id",
* type="integer",
* required=true
* ),
* @SWG\Response(
* response=200,
* description="A single $LOWER_NAME$",
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/$STUDLY_NAME$"))
* ),
* @SWG\Response(
* response="default",
* description="an ""unexpected"" error"
* )
* )
*/
public function show($STUDLY_NAME$Request $request)
{
return $this->itemResponse($request->entity());
}
/**
* @SWG\Post(
* path="/$LOWER_NAME$",
* summary="Create a $LOWER_NAME$",
* operationId="create$STUDLY_NAME$",
* tags={"$LOWER_NAME$"},
* @SWG\Parameter(
* in="body",
* name="$LOWER_NAME$",
* @SWG\Schema(ref="#/definitions/$STUDLY_NAME$")
* ),
* @SWG\Response(
* response=200,
* description="New $LOWER_NAME$",
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/$STUDLY_NAME$"))
* ),
* @SWG\Response(
* response="default",
* description="an ""unexpected"" error"
* )
* )
*/
public function store(Create$STUDLY_NAME$Request $request)
{
$$LOWER_NAME$ = $this->$LOWER_NAME$Repo->save($request->input());
return $this->itemResponse($$LOWER_NAME$);
}
/**
* @SWG\Put(
* path="/$LOWER_NAME$/{$LOWER_NAME$_id}",
* summary="Update a $LOWER_NAME$",
* operationId="update$STUDLY_NAME$",
* tags={"$LOWER_NAME$"},
* @SWG\Parameter(
* in="path",
* name="$LOWER_NAME$_id",
* type="integer",
* required=true
* ),
* @SWG\Parameter(
* in="body",
* name="$LOWER_NAME$",
* @SWG\Schema(ref="#/definitions/$STUDLY_NAME$")
* ),
* @SWG\Response(
* response=200,
* description="Updated $LOWER_NAME$",
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/$STUDLY_NAME$"))
* ),
* @SWG\Response(
* response="default",
* description="an ""unexpected"" error"
* )
* )
*/
public function update(Update$STUDLY_NAME$Request $request, $publicId)
{
if ($request->action) {
return $this->handleAction($request);
}
$$LOWER_NAME$ = $this->$LOWER_NAME$Repo->save($request->input(), $request->entity());
return $this->itemResponse($$LOWER_NAME$);
}
/**
* @SWG\Delete(
* path="/$LOWER_NAME$/{$LOWER_NAME$_id}",
* summary="Delete a $LOWER_NAME$",
* operationId="delete$STUDLY_NAME$",
* tags={"$LOWER_NAME$"},
* @SWG\Parameter(
* in="path",
* name="$LOWER_NAME$_id",
* type="integer",
* required=true
* ),
* @SWG\Response(
* response=200,
* description="Deleted $LOWER_NAME$",
* @SWG\Schema(type="object", @SWG\Items(ref="#/definitions/$STUDLY_NAME$"))
* ),
* @SWG\Response(
* response="default",
* description="an ""unexpected"" error"
* )
* )
*/
public function destroy(Update$STUDLY_NAME$Request $request)
{
$$LOWER_NAME$ = $request->entity();
$this->$LOWER_NAME$Repo->delete($$LOWER_NAME$);
return $this->itemResponse($$LOWER_NAME$);
}
}

View file

@ -0,0 +1,17 @@
<?php
namespace $NAMESPACE$;
use App\Providers\AuthServiceProvider;
class $STUDLY_NAME$AuthProvider extends AuthServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
\Modules\$STUDLY_NAME$\Models\$STUDLY_NAME$::class => \Modules\$STUDLY_NAME$\Policies\$STUDLY_NAME$Policy::class,
];
}

View file

@ -0,0 +1,68 @@
<?php
namespace $NAMESPACE$;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class $CLASS$ extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $name = '$COMMAND_NAME$';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
//
}
/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
{
return [
['example', InputArgument::REQUIRED, 'An example argument.'],
];
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null],
];
}
}

View file

@ -0,0 +1,15 @@
{
"name": "$VENDOR$/$LOWER_NAME$",
"description": "",
"authors": [
{
"name": "$AUTHOR_NAME$",
"email": "$AUTHOR_EMAIL$"
}
],
"autoload": {
"psr-4": {
"$MODULE_NAMESPACE$\\$STUDLY_NAME$\\": ""
}
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace $CLASS_NAMESPACE$;
use Illuminate\Routing\Controller;
class $CLASS$ extends Controller
{
}

View file

@ -0,0 +1,131 @@
<?php
namespace $CLASS_NAMESPACE$;
use Auth;
use App\Http\Controllers\BaseController;
use App\Services\DatatableService;
use Modules\$STUDLY_NAME$\Datatables\$STUDLY_NAME$Datatable;
use Modules\$STUDLY_NAME$\Repositories\$STUDLY_NAME$Repository;
use Modules\$STUDLY_NAME$\Http\Requests\$STUDLY_NAME$Request;
use Modules\$STUDLY_NAME$\Http\Requests\Create$STUDLY_NAME$Request;
use Modules\$STUDLY_NAME$\Http\Requests\Update$STUDLY_NAME$Request;
class $CLASS$ extends BaseController
{
protected $$STUDLY_NAME$Repo;
//protected $entityType = '$LOWER_NAME$';
public function __construct($STUDLY_NAME$Repository $$LOWER_NAME$Repo)
{
//parent::__construct();
$this->$LOWER_NAME$Repo = $$LOWER_NAME$Repo;
}
/**
* Display a listing of the resource.
* @return Response
*/
public function index()
{
return view('list_wrapper', [
'entityType' => '$LOWER_NAME$',
'datatable' => new $STUDLY_NAME$Datatable(),
'title' => mtrans('$LOWER_NAME$', '$LOWER_NAME$_list'),
]);
}
public function datatable(DatatableService $datatableService)
{
$search = request()->input('sSearch');
$userId = Auth::user()->filterId();
$datatable = new $STUDLY_NAME$Datatable();
$query = $this->$LOWER_NAME$Repo->find($search, $userId);
return $datatableService->createDatatable($datatable, $query);
}
/**
* Show the form for creating a new resource.
* @return Response
*/
public function create($STUDLY_NAME$Request $request)
{
$data = [
'$LOWER_NAME$' => null,
'method' => 'POST',
'url' => '$LOWER_NAME$',
'title' => mtrans('$LOWER_NAME$', 'new_$LOWER_NAME$'),
];
return view('$LOWER_NAME$::edit', $data);
}
/**
* Store a newly created resource in storage.
* @param Request $request
* @return Response
*/
public function store(Create$STUDLY_NAME$Request $request)
{
$$LOWER_NAME$ = $this->$LOWER_NAME$Repo->save($request->input());
return redirect()->to($$LOWER_NAME$->present()->editUrl)
->with('message', mtrans('$LOWER_NAME$', 'created_$LOWER_NAME$'));
}
/**
* Show the form for editing the specified resource.
* @return Response
*/
public function edit($STUDLY_NAME$Request $request)
{
$$LOWER_NAME$ = $request->entity();
$data = [
'$LOWER_NAME$' => $$LOWER_NAME$,
'method' => 'PUT',
'url' => '$LOWER_NAME$/' . $$LOWER_NAME$->public_id,
'title' => mtrans('$LOWER_NAME$', 'edit_$LOWER_NAME$'),
];
return view('$LOWER_NAME$::edit', $data);
}
/**
* Show the form for editing a resource.
* @return Response
*/
public function show($STUDLY_NAME$Request $request)
{
return redirect()->to("$LOWER_NAME$/{$request->$LOWER_NAME$}/edit");
}
/**
* Update the specified resource in storage.
* @param Request $request
* @return Response
*/
public function update(Update$STUDLY_NAME$Request $request)
{
$$LOWER_NAME$ = $this->$LOWER_NAME$Repo->save($request->input(), $request->entity());
return redirect()->to($$LOWER_NAME$->present()->editUrl)
->with('message', mtrans('$LOWER_NAME$', 'updated_$LOWER_NAME$'));
}
/**
* Update multiple resources
*/
public function bulk()
{
$action = request()->input('action');
$ids = request()->input('public_id') ?: request()->input('ids');
$count = $this->$LOWER_NAME$Repo->bulk($ids, $action);
return redirect()->to('$LOWER_NAME$')
->with('message', mtrans('$LOWER_NAME$', $action . '_$LOWER_NAME$_complete'));
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace $NAMESPACE$;
class Create$CLASS$Request extends $CLASS$Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return $this->user()->can('create', '$LOWER_NAME$');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
];
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace $NAMESPACE$;
use Utils;
use URL;
use Auth;
use App\Ninja\Datatables\EntityDatatable;
class $CLASS$Datatable extends EntityDatatable
{
public $entityType = '$LOWER_NAME$';
public $sortCol = 1;
public function columns()
{
return [
$DATATABLE_COLUMNS$
[
'created_at',
function ($model) {
return Utils::fromSqlDateTime($model->created_at);
}
],
];
}
public function actions()
{
return [
[
mtrans('$LOWER_NAME$', 'edit_$LOWER_NAME$'),
function ($model) {
return URL::to("$LOWER_NAME$/{$model->public_id}/edit");
},
function ($model) {
return Auth::user()->can('editByOwner', ['$LOWER_NAME$', $model->user_id]);
}
],
];
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace $NAMESPACE$;
use Illuminate\Queue\SerializesModels;
class $CLASS$
{
use SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the channels the event should be broadcast on.
*
* @return array
*/
public function broadcastOn()
{
return [];
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace $NAMESPACE$;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Bus\Queueable;
class $CLASS$ implements ShouldQueue
{
use InteractsWithQueue, SerializesModels, Queueable;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
//
}
}

View file

@ -0,0 +1,17 @@
{
"name": "$STUDLY_NAME$",
"alias": "$LOWER_NAME$",
"description": "",
"keywords": [],
"active": 1,
"order": 0,
"providers": [
"$MODULE_NAMESPACE$\\$STUDLY_NAME$\\Providers\\$STUDLY_NAME$ServiceProvider"
],
"aliases":{},
"files": [
"start.php"
],
"icon": "th-large",
"plural": "$LOWER_NAME$"
}

View file

@ -0,0 +1,19 @@
<?php
$LANG = array(
'$LOWER_NAME$' => '$STUDLY_NAME$',
'$LOWER_NAME$_list' => '$STUDLY_NAME$ List',
'archive_$LOWER_NAME$' => 'Archive $STUDLY_NAME$',
'delete_$LOWER_NAME$' => 'Delete $STUDLY_NAME$',
'edit_$LOWER_NAME$' => 'Edit $STUDLY_NAME$',
'restore_$LOWER_NAME$' => 'Restore $STUDLY_NAME$',
'new_$LOWER_NAME$' => 'New $STUDLY_NAME$',
'created_$LOWER_NAME$' => 'Successfully created $LOWER_NAME$',
'updated_$LOWER_NAME$' => 'Successfully updated $LOWER_NAME$',
);
return $LANG;
?>

View file

@ -0,0 +1,31 @@
<?php
namespace $NAMESPACE$;
use $EVENTNAME$;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class $CLASS$
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param \$EVENTNAME$ $event
* @return void
*/
public function handle(\$EVENTNAME$ $event)
{
//
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace $NAMESPACE$;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class $CLASS$ extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->view('view.name');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace $NAMESPACE$;
use Closure;
use Illuminate\Http\Request;
class $CLASS$
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
return $next($request);
}
}

View file

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class $CLASS$ extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('$TABLE$', function (Blueprint $table) {
$FIELDS_UP$
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('$TABLE$', function (Blueprint $table) {
$FIELDS_DOWN$
});
}
}

View file

@ -0,0 +1,44 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class $CLASS$ extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create(strtolower('$TABLE$'), function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('user_id')->index();
$table->unsignedInteger('account_id')->index();
$table->unsignedInteger('client_id')->index()->nullable();
$FIELDS$
$table->timestamps();
$table->softDeletes();
$table->boolean('is_deleted')->default(false);
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('client_id')->references('id')->on('clients')->onDelete('cascade');
$table->unsignedInteger('public_id')->index();
$table->unique( ['account_id', 'public_id'] );
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists(strtolower('$TABLE$'));
}
}

View file

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class $CLASS$ extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('$TABLE$', function (Blueprint $table) {
$FIELDS_UP$
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('$TABLE$', function (Blueprint $table) {
$FIELDS_DOWN$
});
}
}

View file

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class $CLASS$ extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::dropIfExists('$TABLE$');
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::create('$TABLE$', function (Blueprint $table) {
$table->increments('id');
$FIELDS$
$table->timestamps();
});
}
}

View file

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class $CLASS$ extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
//
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace $NAMESPACE$;
use App\Models\EntityModel;
use Laracasts\Presenter\PresentableTrait;
use Illuminate\Database\Eloquent\SoftDeletes;
class $CLASS$ extends EntityModel
{
use PresentableTrait;
use SoftDeletes;
/**
* @var string
*/
protected $presenter = 'Modules\$CLASS$\Presenters\$CLASS$Presenter';
/**
* @var string
*/
protected $fillable = $FILLABLE$;
/**
* @var string
*/
protected $table = '$LOWER_NAME$';
public function getEntityType()
{
return '$LOWER_NAME$';
}
}

View file

@ -0,0 +1,61 @@
<?php
namespace $NAMESPACE$;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class $CLASS$ extends Notification
{
use Queueable;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->line('The introduction to the notification.')
->action('Notification Action', 'https://laravel.com')
->line('Thank you for using our application!');
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}

View file

@ -0,0 +1,10 @@
<?php
namespace $NAMESPACE$;
use App\Policies\EntityPolicy;
class $STUDLY_NAME$Policy extends EntityPolicy
{
}

View file

@ -0,0 +1,10 @@
<?php
namespace $NAMESPACE$;
use App\Ninja\Presenters\EntityPresenter;
class $STUDLY_NAME$Presenter extends EntityPresenter
{
}

View file

@ -0,0 +1,44 @@
<?php
namespace $NAMESPACE$;
use App\Providers\AuthServiceProvider;
class $CLASS$ extends AuthServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
\Modules\$STUDLY_NAME$\Models\$STUDLY_NAME$::class => \Modules\$STUDLY_NAME$\Policies\$STUDLY_NAME$Policy::class,
];
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
//
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [];
}
}

View file

@ -0,0 +1,71 @@
<?php
namespace $NAMESPACE$;
use DB;
use Modules\$STUDLY_NAME$\Models\$STUDLY_NAME$;
use App\Ninja\Repositories\BaseRepository;
//use App\Events\$STUDLY_NAME$WasCreated;
//use App\Events\$STUDLY_NAME$WasUpdated;
class $STUDLY_NAME$Repository extends BaseRepository
{
public function getClassName()
{
return 'Modules\$STUDLY_NAME$\Models\$STUDLY_NAME$';
}
public function all()
{
return $STUDLY_NAME$::scope()
->orderBy('created_at', 'desc')
->withTrashed();
}
public function find($filter = null, $userId = false)
{
$query = DB::table('$LOWER_NAME$')
->where('$LOWER_NAME$.account_id', '=', \Auth::user()->account_id)
->select(
$DATABASE_FIELDS$
'$LOWER_NAME$.public_id',
'$LOWER_NAME$.deleted_at',
'$LOWER_NAME$.created_at',
'$LOWER_NAME$.is_deleted',
'$LOWER_NAME$.user_id'
);
$this->applyFilters($query, '$LOWER_NAME$');
if ($userId) {
$query->where('clients.user_id', '=', $userId);
}
/*
if ($filter) {
$query->where();
}
*/
return $query;
}
public function save($data, $$LOWER_NAME$ = null)
{
$entity = $$LOWER_NAME$ ?: $STUDLY_NAME$::createNew();
$entity->fill($data);
$entity->save();
/*
if (!$publicId || intval($publicId) < 0) {
event(new ClientWasCreated($client));
} else {
event(new ClientWasUpdated($client));
}
*/
return $entity;
}
}

View file

@ -0,0 +1,10 @@
<?php
namespace $NAMESPACE$;
use App\Http\Requests\EntityRequest;
class $CLASS$Request extends EntityRequest
{
protected $entityType = '$LOWER_NAME$';
}

View file

@ -0,0 +1,39 @@
<?php
namespace $MODULE_NAMESPACE$\$MODULE$\Providers;
use Illuminate\Routing\Router;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class $NAME$ extends ServiceProvider
{
/**
* The root namespace to assume when generating URLs to actions.
*
* @var string
*/
protected $rootUrlNamespace = '$MODULE_NAMESPACE$\$MODULE$\Http\Controllers';
/**
* Called before routes are registered.
*
* Register any model bindings or pattern based filters.
*
* @param Router $router
* @return void
*/
public function before(Router $router)
{
//
}
/**
* Define the routes for the application.
*
* @return void
*/
public function map(Router $router)
{
// require __DIR__ . '/../Http/routes.php';
}
}

View file

@ -0,0 +1,13 @@
<?php
Route::group(['middleware' => ['web', 'lookup:user', 'auth:user'], 'namespace' => '$MODULE_NAMESPACE$\$STUDLY_NAME$\Http\Controllers'], function()
{
Route::resource('$LOWER_NAME$', '$STUDLY_NAME$Controller');
Route::post('$LOWER_NAME$/bulk', '$STUDLY_NAME$Controller@bulk');
Route::get('api/$LOWER_NAME$', '$STUDLY_NAME$Controller@datatable');
});
Route::group(['middleware' => 'api', 'namespace' => '$MODULE_NAMESPACE$\$STUDLY_NAME$\Http\ApiControllers', 'prefix' => 'api/v1'], function()
{
Route::resource('$LOWER_NAME$', '$STUDLY_NAME$ApiController');
});

View file

@ -0,0 +1,5 @@
<?php
return [
'name' => '$STUDLY_NAME$'
];

View file

@ -0,0 +1,110 @@
<?php
namespace $NAMESPACE$;
use App\Providers\AuthServiceProvider;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
class $CLASS$ extends AuthServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
\Modules\$STUDLY_NAME$\Models\$STUDLY_NAME$::class => \Modules\$STUDLY_NAME$\Policies\$STUDLY_NAME$Policy::class,
];
/**
* Boot the application events.
*
* @return void
*/
public function boot()
{
parent::boot();
$this->registerTranslations();
$this->registerConfig();
$this->registerViews();
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
//
}
/**
* Register config.
*
* @return void
*/
protected function registerConfig()
{
$this->publishes([
__DIR__.'/../$PATH_CONFIG$/config.php' => config_path('$LOWER_NAME$.php'),
]);
$this->mergeConfigFrom(
__DIR__.'/../$PATH_CONFIG$/config.php', '$LOWER_NAME$'
);
}
/**
* Register views.
*
* @return void
*/
public function registerViews()
{
$viewPath = base_path('resources/views/modules/$LOWER_NAME$');
$sourcePath = __DIR__.'/../$PATH_VIEWS$';
$this->publishes([
$sourcePath => $viewPath
]);
$this->loadViewsFrom(array_merge(array_map(function ($path) {
return $path . '/modules/$LOWER_NAME$';
}, \Config::get('view.paths')), [$sourcePath]), '$LOWER_NAME$');
}
/**
* Register translations.
*
* @return void
*/
public function registerTranslations()
{
$langPath = base_path('resources/lang/modules/$LOWER_NAME$');
if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, '$LOWER_NAME$');
} else {
$this->loadTranslationsFrom(__DIR__ .'/../$PATH_LANG$', '$LOWER_NAME$');
}
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [];
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace $NAMESPACE$\Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
class $NAME$ extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Model::unguard();
// $this->call("OthersTableSeeder");
}
}

View file

@ -0,0 +1,15 @@
<?php
/*
|--------------------------------------------------------------------------
| Register Namespaces And Routes
|--------------------------------------------------------------------------
|
| When a module starting, this file will executed automatically. This helps
| to register some namespaces like translator or view. Also this file
| will load the routes file for each module. You may also modify
| this file as you want.
|
*/
require __DIR__ . '/Http/routes.php';

View file

@ -0,0 +1,35 @@
<?php
namespace $NAMESPACE$;
use Modules\$STUDLY_NAME$\Models\$STUDLY_NAME$;
use App\Ninja\Transformers\EntityTransformer;
/**
* @SWG\Definition(definition="$STUDLY_NAME$", @SWG\Xml(name="$STUDLY_NAME$"))
*/
class $STUDLY_NAME$Transformer extends EntityTransformer
{
/**
* @SWG\Property(property="id", type="integer", example=1, readOnly=true)
* @SWG\Property(property="user_id", type="integer", example=1)
* @SWG\Property(property="account_key", type="string", example="123456")
* @SWG\Property(property="updated_at", type="integer", example=1451160233, readOnly=true)
* @SWG\Property(property="archived_at", type="integer", example=1451160233, readOnly=true)
*/
/**
* @param $STUDLY_NAME$ $$LOWER_NAME$
* @return array
*/
public function transform($STUDLY_NAME$ $$LOWER_NAME$)
{
return array_merge($this->getDefaults($$LOWER_NAME$), [
$TRANSFORMER_FIELDS$
'id' => (int) $$LOWER_NAME$->public_id,
'updated_at' => $this->getTimestamp($$LOWER_NAME$->updated_at),
'archived_at' => $this->getTimestamp($$LOWER_NAME$->deleted_at),
]);
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace $NAMESPACE$;
class Update$CLASS$Request extends $CLASS$Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return $this->user()->can('edit', $this->entity());
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
];
}
}

View file

@ -0,0 +1,57 @@
@extends('header')
@section('content')
{!! Former::open($url)
->addClass('col-md-10 col-md-offset-1 warn-on-exit')
->method($method)
->rules([]) !!}
@if ($$LOWER_NAME$)
{!! Former::populate($$LOWER_NAME$) !!}
<div style="display:none">
{!! Former::text('public_id') !!}
</div>
@endif
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div class="panel panel-default">
<div class="panel-body">
$FORM_FIELDS$
</div>
</div>
</div>
</div>
<center class="buttons">
{!! Button::normal(trans('texts.cancel'))
->large()
->asLinkTo(URL::to('/$LOWER_NAME$'))
->appendIcon(Icon::create('remove-circle')) !!}
{!! Button::success(trans('texts.save'))
->submit()
->large()
->appendIcon(Icon::create('floppy-disk')) !!}
</center>
{!! Former::close() !!}
<script type="text/javascript">
$(function() {
$(".warn-on-exit input").first().focus();
})
</script>
@stop

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Module $STUDLY_NAME$</title>
</head>
<body>
@yield('content')
</body>
</html>

View file

@ -1,30 +1,10 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Console;
use App\Jobs\Cron\AutoBillCron;
use App\Jobs\Cron\RecurringInvoicesCron;
use App\Jobs\Cron\SubscriptionCron;
use App\Jobs\Ninja\AdjustEmailQuota;
use App\Jobs\Ninja\CompanySizeCheck;
use App\Jobs\Util\DiskCleanup;
use App\Jobs\Util\ReminderJob;
use App\Jobs\Util\SchedulerCheck;
use App\Jobs\Util\SendFailedEmails;
use App\Jobs\Util\UpdateExchangeRates;
use App\Jobs\Util\VersionCheck;
use App\Utils\Ninja;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Utils;
class Kernel extends ConsoleKernel
{
@ -34,65 +14,46 @@ class Kernel extends ConsoleKernel
* @var array
*/
protected $commands = [
//
'App\Console\Commands\SendRecurringInvoices',
'App\Console\Commands\RemoveOrphanedDocuments',
'App\Console\Commands\ResetData',
'App\Console\Commands\CheckData',
'App\Console\Commands\PruneData',
'App\Console\Commands\CreateTestData',
'App\Console\Commands\CreateLuisData',
'App\Console\Commands\MobileLocalization',
'App\Console\Commands\SendRenewalInvoices',
'App\Console\Commands\ChargeRenewalInvoices',
'App\Console\Commands\SendReminders',
'App\Console\Commands\TestOFX',
'App\Console\Commands\MakeModule',
'App\Console\Commands\MakeClass',
'App\Console\Commands\InitLookup',
'App\Console\Commands\CalculatePayouts',
'App\Console\Commands\UpdateKey',
'App\Console\Commands\ExportMigrations',
];
/**
* Define the application's command schedule.
*
* @param Schedule $schedule
* @param \Illuminate\Console\Scheduling\Schedule $schedule
*
* @return void
*/
protected function schedule(Schedule $schedule)
{
$logFile = storage_path() . '/logs/cron.log';
$schedule->job(new VersionCheck)->daily();
$schedule
->command('ninja:send-invoices --force')
->sendOutputTo($logFile)
->withoutOverlapping()
->hourly();
$schedule->job(new DiskCleanup)->daily()->withoutOverlapping();
$schedule->command('ninja:check-data --database=db-ninja-01')->daily()->withoutOverlapping();
$schedule->job(new ReminderJob)->hourly()->withoutOverlapping();
$schedule->job(new CompanySizeCheck)->daily()->withoutOverlapping();
$schedule->job(new UpdateExchangeRates)->daily()->withoutOverlapping();
$schedule->job(new SubscriptionCron)->daily()->withoutOverlapping();
$schedule->job(new RecurringInvoicesCron)->hourly()->withoutOverlapping();
$schedule->job(new AutoBillCron)->dailyAt('00:30')->withoutOverlapping();
$schedule->job(new SchedulerCheck)->daily()->withoutOverlapping();
/* Run hosted specific jobs */
if (Ninja::isHosted()) {
$schedule->job(new AdjustEmailQuota)->dailyAt('23:00')->withoutOverlapping();
$schedule->job(new SendFailedEmails)->daily()->withoutOverlapping();
$schedule->command('ninja:check-data --database=db-ninja-02')->daily()->withoutOverlapping();
}
if(config('queue.default') == 'database' && Ninja::isSelfHost() && config('ninja.internal_queue_enabled') && !config('ninja.is_docker')) {
$schedule->command('queue:work')->everyMinute()->withoutOverlapping();
$schedule->command('queue:restart')->everyFiveMinutes()->withoutOverlapping();
}
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
$schedule
->command('ninja:send-reminders --force')
->sendOutputTo($logFile)
->daily();
}
}

715
app/Constants.php Normal file
View file

@ -0,0 +1,715 @@
<?php
if (! defined('APP_NAME')) {
define('APP_NAME', env('APP_NAME', 'Invoice Ninja'));
define('APP_DOMAIN', env('APP_DOMAIN', 'invoiceninja.com'));
define('CONTACT_EMAIL', env('MAIL_FROM_ADDRESS'));
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_TASK_STATUS', 'task_status');
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('ENTITY_RECURRING_EXPENSE', 'recurring_expense');
define('ENTITY_CUSTOMER', 'customer');
define('ENTITY_SUBSCRIPTION', 'subscription');
define('ENTITY_PROPOSAL', 'proposal');
define('ENTITY_PROPOSAL_TEMPLATE', 'proposal_template');
define('ENTITY_PROPOSAL_SNIPPET', 'proposal_snippet');
define('ENTITY_PROPOSAL_CATEGORY', 'proposal_category');
define('ENTITY_PROPOSAL_INVITATION', 'proposal_invitation');
$permissionEntities = [
ENTITY_CLIENT,
//ENTITY_CONTACT,
ENTITY_CREDIT,
ENTITY_EXPENSE,
ENTITY_INVOICE,
ENTITY_PAYMENT,
ENTITY_PRODUCT,
ENTITY_PROJECT,
ENTITY_PROPOSAL,
ENTITY_QUOTE,
'reports',
ENTITY_TASK,
ENTITY_VENDOR,
ENTITY_RECURRING_INVOICE,
];
define('PERMISSION_ENTITIES', json_encode($permissionEntities));
define('INVOICE_TYPE_STANDARD', 1);
define('INVOICE_TYPE_QUOTE', 2);
define('INVOICE_ITEM_TYPE_STANDARD', 1);
define('INVOICE_ITEM_TYPE_TASK', 2);
define('INVOICE_ITEM_TYPE_PENDING_GATEWAY_FEE', 3);
define('INVOICE_ITEM_TYPE_PAID_GATEWAY_FEE', 4);
define('INVOICE_ITEM_TYPE_LATE_FEE', 5);
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', 5000);
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('MAX_EMAILS_SENT_PER_DAY', 300);
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('DEFAULT_BANK_OFX_VERSION', 102);
define('DEFAULT_BANK_APP_VERSION', 2500);
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_INVOICEPLANE', 'InvoicePlane');
define('IMPORT_HARVEST', 'Harvest');
define('IMPORT_STRIPE', 'Stripe');
define('IMPORT_PANCAKE', 'Pancake');
define('MAX_NUM_CLIENTS', 100);
define('MAX_NUM_CLIENTS_PRO', 40000);
define('MAX_NUM_CLIENTS_LEGACY', 500);
define('MAX_INVOICE_AMOUNT', 10000000000);
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', -1);
define('INVOICE_STATUS_UNPAID', -2);
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_PENDING', 2);
define('EXPENSE_STATUS_INVOICED', 3);
define('EXPENSE_STATUS_BILLED', 4);
define('EXPENSE_STATUS_PAID', 5);
define('EXPENSE_STATUS_UNPAID', 6);
define('CUSTOM_DESIGN1', 11);
define('CUSTOM_DESIGN2', 12);
define('CUSTOM_DESIGN3', 13);
define('FREQUENCY_WEEKLY', 1);
define('FREQUENCY_TWO_WEEKS', 2);
define('FREQUENCY_FOUR_WEEKS', 3);
define('FREQUENCY_MONTHLY', 4);
define('FREQUENCY_TWO_MONTHS', 5);
define('FREQUENCY_THREE_MONTHS', 6);
define('FREQUENCY_FOUR_MONTHS', 7);
define('FREQUENCY_SIX_MONTHS', 8);
define('FREQUENCY_ANNUALLY', 9);
define('FREQUENCY_TWO_YEARS', 10);
define('REPORT_FREQUENCY_DAILY', 'daily');
define('REPORT_FREQUENCY_WEEKLY', 'weekly');
define('REPORT_FREQUENCY_BIWEEKLY', 'biweekly');
define('REPORT_FREQUENCY_MONTHLY', 'monthly');
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_DB_SERVER', 'dbServer');
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_TWO_CHECKOUT', 27);
define('GATEWAY_BEANSTREAM', 29);
define('GATEWAY_PSIGATE', 30);
define('GATEWAY_BITPAY', 42);
define('GATEWAY_DWOLLA', 43);
define('GATEWAY_CHECKOUT_COM', 47);
define('GATEWAY_CYBERSOURCE', 49);
define('GATEWAY_PAYTRACE', 56);
define('GATEWAY_WEPAY', 60);
define('GATEWAY_BRAINTREE', 61);
define('GATEWAY_CUSTOM1', 62);
define('GATEWAY_GOCARDLESS', 64);
define('GATEWAY_PAYMILL', 66);
define('GATEWAY_CUSTOM2', 67);
define('GATEWAY_CUSTOM3', 68);
// The customer exists, but only as a local concept
// The remote gateway doesn't understand the concept of customers
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('EVENT_UPDATE_CLIENT', 10);
define('EVENT_DELETE_CLIENT', 11);
define('EVENT_DELETE_PAYMENT', 12);
define('EVENT_UPDATE_VENDOR', 13);
define('EVENT_DELETE_VENDOR', 14);
define('EVENT_CREATE_EXPENSE', 15);
define('EVENT_UPDATE_EXPENSE', 16);
define('EVENT_DELETE_EXPENSE', 17);
define('EVENT_CREATE_TASK', 18);
define('EVENT_UPDATE_TASK', 19);
define('EVENT_DELETE_TASK', 20);
define('EVENT_APPROVE_QUOTE', 21);
define('REQUESTED_PRO_PLAN', 'REQUESTED_PRO_PLAN');
define('NINJA_ACCOUNT_KEY', env('NINJA_ACCOUNT_KEY', 'zg4ylmzDkdkPOT8yoKQw9LTWaoZJx79h'));
define('NINJA_ACCOUNT_EMAIL', env('NINJA_ACCOUNT_EMAIL', 'contact@invoiceninja.com'));
define('NINJA_LICENSE_ACCOUNT_KEY', 'AsFmBAeLXF0IKf7tmi0eiyZfmWW9hxMT');
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', 'https://invoice-ninja.readthedocs.io/en/latest'));
define('NINJA_DATE', '2000-01-01');
define('NINJA_VERSION', '4.5.40' . env('NINJA_VERSION_SUFFIX'));
define('NINJA_TERMS_VERSION', '1.0.1');
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/us/app/invoice-ninja/id1435514417?ls=1&mt=8');
define('NINJA_ANDROID_APP_URL', 'https://play.google.com/store/apps/details?id=com.invoiceninja.flutter');
define('RELEASES_URL', env('RELEASES_URL', 'https://github.com/invoiceninja/invoiceninja/releases'));
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('IP_LOOKUP_URL', env('IP_LOOKUP_URL', 'http://whatismyipaddress.com/ip/'));
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://westus.api.cognitive.microsoft.com/luis/v2.0/apps');
define('SKYPE_API_URL', 'https://apis.skype.com/v3');
define('MSBOT_STATE_URL', 'https://state.botframework.com/v3');
define('INVOICEPLANE_IMPORT', 'https://github.com/turbo124/Plane2Ninja');
define('TIME_TRACKER_USER_AGENT', 'Time Tracker');
define('BOT_PLATFORM_WEB_APP', 'WebApp');
define('BOT_PLATFORM_SKYPE', 'Skype');
define('BLANK_IMAGE', 'data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=');
define('DB_NINJA_LOOKUP', 'db-ninja-0');
define('DB_NINJA_1', 'db-ninja-1');
define('DB_NINJA_2', 'db-ninja-2');
define('COUNT_FREE_DESIGNS', 4);
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', 10));
define('PLAN_PRICE_ENTERPRISE_MONTHLY_2', env('PLAN_PRICE_ENTERPRISE_MONTHLY_2', 14));
define('PLAN_PRICE_ENTERPRISE_MONTHLY_5', env('PLAN_PRICE_ENTERPRISE_MONTHLY_5', 26));
define('PLAN_PRICE_ENTERPRISE_MONTHLY_10', env('PLAN_PRICE_ENTERPRISE_MONTHLY_10', 36));
define('PLAN_PRICE_ENTERPRISE_MONTHLY_20', env('PLAN_PRICE_ENTERPRISE_MONTHLY_20', 44));
define('WHITE_LABEL_PRICE', env('WHITE_LABEL_PRICE', 30));
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', env('TEST_USERNAME', 'user@example.com'));
define('TEST_PERMISSIONS_USERNAME', env('TEST_PERMISSIONS_USERNAME', 'permissions@example.com'));
define('TEST_PASSWORD', 'password');
define('API_SECRET', 'API_SECRET');
define('DEFAULT_API_PAGE_SIZE', 15);
define('MAX_API_PAGE_SIZE', 5000);
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_TYPE_ALIPAY', 28);
define('PAYMENT_TYPE_SOFORT', 29);
define('PAYMENT_TYPE_SEPA', 30);
define('PAYMENT_TYPE_GOCARDLESS', 31);
define('PAYMENT_TYPE_BITCOIN', 32);
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_CUSTOM1', 6);
define('GATEWAY_TYPE_ALIPAY', 7);
define('GATEWAY_TYPE_SOFORT', 8);
define('GATEWAY_TYPE_SEPA', 9);
define('GATEWAY_TYPE_GOCARDLESS', 10);
define('GATEWAY_TYPE_APPLE_PAY', 11);
define('GATEWAY_TYPE_CUSTOM2', 12);
define('GATEWAY_TYPE_CUSTOM3', 13);
define('GATEWAY_TYPE_TOKEN', 'token');
define('TEMPLATE_INVOICE', 'invoice');
define('TEMPLATE_QUOTE', 'quote');
define('TEMPLATE_PROPOSAL', 'proposal');
define('TEMPLATE_PARTIAL', 'partial');
define('TEMPLATE_PAYMENT', 'payment');
define('TEMPLATE_REMINDER1', 'reminder1');
define('TEMPLATE_REMINDER2', 'reminder2');
define('TEMPLATE_REMINDER3', 'reminder3');
define('TEMPLATE_REMINDER4', 'reminder4');
define('CUSTOM_MESSAGE_DASHBOARD', 'dashboard');
define('CUSTOM_MESSAGE_UNPAID_INVOICE', 'unpaid_invoice');
define('CUSTOM_MESSAGE_PAID_INVOICE', 'paid_invoice');
define('CUSTOM_MESSAGE_UNAPPROVED_QUOTE', 'unapproved_quote');
define('CUSTOM_MESSAGE_APPROVED_QUOTE', 'approved_quote');
define('CUSTOM_MESSAGE_UNAPPROVED_PROPOSAL', 'unapproved_proposal');
define('CUSTOM_MESSAGE_APPROVED_PROPOSAL', 'approved_proposal');
define('RESET_FREQUENCY_DAILY', 1);
define('RESET_FREQUENCY_WEEKLY', 2);
define('RESET_FREQUENCY_MONTHLY', 3);
define('RESET_FREQUENCY_QUATERLY', 4);
define('RESET_FREQUENCY_YEARLY', 5);
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('ADDRESS_BILLING', 'billing_address');
define('ADDRESS_SHIPPING', 'shipping_address');
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_ACCOUNT_COUNT', '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');
define('SUBSCRIPTION_FORMAT_JSON', 'JSON');
define('SUBSCRIPTION_FORMAT_UBL', 'UBL');
// 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_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');
define('INVOICE_FIELDS_PRODUCT', 'product_fields');
define('INVOICE_FIELDS_TASK', 'task_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',
'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));
// Fix for mPDF: https://github.com/kartik-v/yii2-mpdf/issues/9
define('_MPDF_TTFONTDATAPATH', storage_path('framework/cache/'));
function uctrans($text, $data = [])
{
$locale = Session::get(SESSION_LOCALE);
$text = trans($text, $data);
return $locale == 'en' ? ucwords($text) : $text;
}
function utrans($text, $data = [])
{
$locale = Session::get(SESSION_LOCALE);
$text = trans($text, $data);
return $locale == 'en' ? strtoupper($text) : $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, $replace = [])
{
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, $replace);
if ($key != $value) {
return $value;
}
}
return trans("texts.{$text}");
}
}

33
app/Constants/Domain.php Normal file
View file

@ -0,0 +1,33 @@
<?php
namespace App\Constants;
use App\Libraries\Utils;
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);
}
}

View file

@ -1,51 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\DataMapper\Analytics;
use Turbo124\Beacon\ExampleMetric\GenericCounter;
class AccountCreated extends GenericCounter
{
/**
* The type of Sample.
*
* Monotonically incrementing counter
*
* - counter
*
* @var string
*/
public $type = 'counter';
/**
* The name of the counter.
* @var string
*/
public $name = 'account.created';
/**
* The datetime of the counter measurement.
*
* date("Y-m-d H:i:s")
*
* @var DateTime
*/
public $datetime;
/**
* The increment amount... should always be
* set to 0.
*
* @var int
*/
public $metric = 0;
}

Some files were not shown because too many files have changed in this diff Show more