Compare commits
No commits in common. "lastFOSS" and "v5.5.51" have entirely different histories.
5204 changed files with 3228536 additions and 502387 deletions
3
.babelrc
Normal file
3
.babelrc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"plugins": ["@babel/plugin-proposal-class-properties"]
|
||||
}
|
||||
3
.bowerrc
3
.bowerrc
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"directory": "./resources/assets/bower"
|
||||
}
|
||||
5
.codacy.yml
Normal file
5
.codacy.yml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Codacy configuration file for Invoice Ninja
|
||||
|
||||
exclude_paths:
|
||||
- 'public/css/*'
|
||||
- 'public/js/*'
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
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"
|
||||
15
.editorconfig
Normal file
15
.editorconfig
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
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
Normal file
23
.env.ci
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
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
|
||||
27
.env.dusk.example
Normal file
27
.env.dusk.example
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
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=
|
||||
131
.env.example
131
.env.example
|
|
@ -1,107 +1,70 @@
|
|||
APP_NAME="Invoice Ninja"
|
||||
APP_ENV=production
|
||||
APP_KEY=base64:RR++yx2rJ9kdxbdh3+AmbHLDQu+Q76i++co9Y8ybbno=
|
||||
APP_DEBUG=false
|
||||
APP_URL=http://www.ninja.test
|
||||
APP_KEY=SomeRandomStringSomeRandomString
|
||||
APP_CIPHER=AES-256-CBC
|
||||
APP_LOCALE=en
|
||||
|
||||
DB_TYPE=mysql
|
||||
DB_STRICT=false
|
||||
APP_URL=http://localhost
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
MULTI_DB_ENABLED=false
|
||||
|
||||
DB_HOST=localhost
|
||||
DB_DATABASE=ninja
|
||||
DB_USERNAME=ninja
|
||||
DB_PASSWORD=ninja
|
||||
DB_PORT=3306
|
||||
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_PORT=587
|
||||
MAIL_ENCRYPTION=tls
|
||||
MAIL_HOST
|
||||
MAIL_USERNAME
|
||||
MAIL_FROM_ADDRESS
|
||||
MAIL_FROM_NAME
|
||||
MAIL_PASSWORD
|
||||
DEMO_MODE=false
|
||||
|
||||
MAILGUN_DOMAIN=
|
||||
MAILGUN_SECRET=
|
||||
BROADCAST_DRIVER=log
|
||||
LOG_CHANNEL=stack
|
||||
CACHE_DRIVER=file
|
||||
QUEUE_CONNECTION=sync
|
||||
SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
#POSTMARK_API_TOKEN=
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
PHANTOMJS_CLOUD_KEY='a-demo-key-with-low-quota-per-ip-address'
|
||||
#PHANTOMJS_BIN_PATH=/usr/local/bin/phantomjs
|
||||
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'
|
||||
|
||||
LOG=single
|
||||
POSTMARK_API_TOKEN=
|
||||
REQUIRE_HTTPS=false
|
||||
API_SECRET=password
|
||||
|
||||
#TRUSTED_PROXIES=
|
||||
GOOGLE_MAPS_API_KEY=
|
||||
ERROR_EMAIL=
|
||||
TRUSTED_PROXIES=
|
||||
|
||||
#SESSION_DRIVER=
|
||||
#SESSION_DOMAIN=
|
||||
#SESSION_ENCRYPT=
|
||||
#SESSION_SECURE=
|
||||
NINJA_ENVIRONMENT=selfhost
|
||||
|
||||
#CACHE_DRIVER=
|
||||
#CACHE_HOST=
|
||||
#REDIS_HOST=
|
||||
#CACHE_PORT1=
|
||||
#CACHE_PORT2=
|
||||
#options - snappdf / phantom / hosted_ninja
|
||||
PDF_GENERATOR=hosted_ninja
|
||||
|
||||
#GOOGLE_CLIENT_ID=
|
||||
#GOOGLE_CLIENT_SECRET=
|
||||
#GOOGLE_OAUTH_REDIRECT=http://ninja.test/auth/google
|
||||
PHANTOMJS_KEY='a-demo-key-with-low-quota-per-ip-address'
|
||||
PHANTOMJS_SECRET=secret
|
||||
|
||||
GOOGLE_MAPS_ENABLED=true
|
||||
#GOOGLE_MAPS_API_KEY=
|
||||
UPDATE_SECRET=secret
|
||||
|
||||
# Create a cookie to stay logged in
|
||||
#REMEMBER_ME_ENABLED=true
|
||||
DELETE_PDF_DAYS=60
|
||||
DELETE_BACKUP_DAYS=60
|
||||
|
||||
# Immediately expire cookie on the browser closing
|
||||
#SESSION_EXPIRE_ON_CLOSE=false
|
||||
COMPOSER_AUTH='{"github-oauth": {"github.com": "${{ secrets.GITHUB_TOKEN }}"}}'
|
||||
|
||||
# The app automatically logs the user out after this number of seconds
|
||||
#AUTO_LOGOUT_SECONDS=28800
|
||||
GOOGLE_PLAY_PACKAGE_NAME=
|
||||
APPSTORE_PASSWORD=
|
||||
|
||||
#S3_KEY=
|
||||
#S3_SECRET=
|
||||
#S3_REGION=
|
||||
#S3_BUCKET=
|
||||
MICROSOFT_CLIENT_ID=
|
||||
MICROSOFT_CLIENT_SECRET=
|
||||
MICROSOFT_REDIRECT_URI=
|
||||
|
||||
#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={}
|
||||
APPLE_CLIENT_ID=
|
||||
APPLE_CLIENT_SECRET=
|
||||
APPLE_REDIRECT_URI=
|
||||
|
|
|
|||
19
.env.travis
19
.env.travis
|
|
@ -1,25 +1,24 @@
|
|||
APP_ENV=development
|
||||
APP_ENV=local
|
||||
APP_DEBUG=false
|
||||
APP_URL=http://ninja.test
|
||||
APP_URL=http://127.0.0.1:8000
|
||||
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_DATABASE0=ninja0
|
||||
DB_DATABASE1=ninja
|
||||
DB_DATABASE2=ninja2
|
||||
DB_CONNECTION=db-ninja-01
|
||||
DB_DATABASE1=ninja01
|
||||
DB_DATABASE2=ninja02
|
||||
|
||||
MAIL_DRIVER=log
|
||||
MAIL_MAILER=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
9
.gitattributes
vendored
|
|
@ -1,8 +1,5 @@
|
|||
* text=auto
|
||||
*.css linguist-vendored
|
||||
*.less linguist-vendored
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.codeclimate.yml export-ignore
|
||||
.travis.yml export-ignore
|
||||
.styleci.yml export-ignore
|
||||
*.scss linguist-vendored
|
||||
*.js linguist-vendored
|
||||
CHANGELOG.md export-ignore
|
||||
|
|
|
|||
41
.github/ISSUE_TEMPLATE/bug_report.md
vendored
41
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -1,41 +0,0 @@
|
|||
---
|
||||
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**
|
||||
24
.github/ISSUE_TEMPLATE/feature_request.md
vendored
24
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
|
@ -1,24 +0,0 @@
|
|||
---
|
||||
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.
|
||||
110
.github/workflows/phpunit.yml
vendored
Normal file
110
.github/workflows/phpunit.yml
vendored
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- v5-develop
|
||||
pull_request:
|
||||
branches:
|
||||
- v5-develop
|
||||
|
||||
name: phpunit
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
strategy:
|
||||
matrix:
|
||||
operating-system: ['ubuntu-20.04', 'ubuntu-22.04']
|
||||
php-versions: ['8.1']
|
||||
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 systemctl start mysql.service
|
||||
- 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/snappdf download
|
||||
vendor/bin/phpunit --testdox
|
||||
env:
|
||||
DB_PORT: ${{ job.services.mysql.ports[3306] }}
|
||||
PHP_CS_FIXER_IGNORE_ENV: true
|
||||
77
.github/workflows/release.yml
vendored
Normal file
77
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
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: 8.1
|
||||
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 React FrontEnd
|
||||
run: |
|
||||
git clone https://${{secrets.commit_secret}}@github.com/invoiceninja/ui.git
|
||||
cd ui
|
||||
git checkout main
|
||||
npm i
|
||||
npm run build
|
||||
cp -r dist/react/* ../public/react
|
||||
cd ..
|
||||
rm -rf ui
|
||||
php artisan ninja:react
|
||||
|
||||
- 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
|
||||
sudo rm .env
|
||||
|
||||
- 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
72
.gitignore
vendored
|
|
@ -1,46 +1,34 @@
|
|||
/config/staging
|
||||
/config/development
|
||||
/config/production
|
||||
/config/fortrabbit
|
||||
/config/ubuntu
|
||||
/config/packages/anahkiasen/rocketeer/
|
||||
/public/logo
|
||||
/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
|
||||
|
||||
/ninja.sublime-project
|
||||
/ninja.sublime-workspace
|
||||
/.phpstorm.meta.php
|
||||
/_ide_helper.php
|
||||
/public/hot
|
||||
/public/storage
|
||||
/public/react
|
||||
/storage/*.key
|
||||
/storage/debugbar
|
||||
/storage/*
|
||||
/vendor
|
||||
/.idea
|
||||
/.project
|
||||
tests/_output/
|
||||
tests/_bootstrap.php
|
||||
tests/_support/_generated/
|
||||
/.vscode
|
||||
/.vagrant
|
||||
/tests/_output
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
local_version.txt
|
||||
.env
|
||||
.phpunit.result.cache
|
||||
|
||||
# composer stuff
|
||||
/c3.php
|
||||
/resources/assets/bower
|
||||
/public/logo
|
||||
.env.dusk.local
|
||||
/public/vendors/*
|
||||
*.log
|
||||
|
||||
_ide_helper.php
|
||||
storage/version.txt
|
||||
storage/framework/.DS_Store
|
||||
# Ignore local migrations
|
||||
storage/migrations
|
||||
nbproject
|
||||
|
||||
.php_cs.cache
|
||||
public/test.pdf
|
||||
public/storage/test.pdf
|
||||
/Modules
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteRule "^.env" - [F,L]
|
||||
RewriteRule "^storage" - [F,L]
|
||||
# RewriteRule "^storage" - [F,L]
|
||||
RewriteRule ^(.well-known)($|/) - [L]
|
||||
|
||||
# https://coderwall.com/p/erbaig/laravel-s-htaccess-to-remove-public-from-url
|
||||
# RewriteRule ^(.*)$ public/$1 [L]
|
||||
RewriteRule ^(.*)$ public/$1 [L]
|
||||
</IfModule>
|
||||
|
||||
# https://github.com/h5bp/server-configs-apache/blob/master/dist/.htaccess
|
||||
|
|
|
|||
19
.php_cs
Normal file
19
.php_cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?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
98
.php_cs.dist
|
|
@ -1,98 +0,0 @@
|
|||
<?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);
|
||||
6
.prettierrc
Normal file
6
.prettierrc
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"arrowParens": "always"
|
||||
}
|
||||
18
.styleci.yml
18
.styleci.yml
|
|
@ -1,18 +0,0 @@
|
|||
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
138
.travis.yml
|
|
@ -1,138 +0,0 @@
|
|||
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
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
# 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
vendored
43
Gruntfile.js
vendored
|
|
@ -1,43 +0,0 @@
|
|||
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
88
LICENSE
|
|
@ -1,47 +1,47 @@
|
|||
Copyright (c) 2018 by Hillel Coren
|
||||
Invoice Ninja * https://www.invoiceninja.com
|
||||
"CREATE. SEND. GET PAID"
|
||||
Elastic License 2.0 (ELv2)
|
||||
Elastic License
|
||||
|
||||
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.
|
||||
Acceptance
|
||||
By using the software, you agree to all of the terms 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.
|
||||
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
|
||||
|
||||
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.
|
||||
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 licensor’s 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/
|
||||
193
README.md
193
README.md
|
|
@ -1,96 +1,97 @@
|
|||
<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
|
||||
|
||||
[](https://travis-ci.org/invoiceninja/invoiceninja)
|
||||
[](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.
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/hillelcoren/invoice-ninja/master/public/images/round_logo.png" alt="Sublime's custom image"/>
|
||||
</p>
|
||||
|
||||

|
||||
[](https://www.codacy.com/gh/turbo124/invoiceninja/dashboard?utm_source=github.com&utm_medium=referral&utm_content=turbo124/invoiceninja&utm_campaign=Badge_Grade)
|
||||
<a href="https://cla-assistant.io/invoiceninja/invoiceninja"><img src="https://cla-assistant.io/readme/badge/invoiceninja/invoiceninja" alt="CLA assistant" /></a>
|
||||
|
||||
# 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.
|
||||
|
||||
All Pro and Enterprise features from the hosted app are included in the open-code. We offer a $30 per year white-label license to remove the Invoice Ninja branding from client facing parts of the app.
|
||||
|
||||
* [Videos](https://www.youtube.com/@appinvoiceninja)
|
||||
* [API Documentation](https://app.swaggerhub.com/apis/invoiceninja/invoiceninja)
|
||||
* [APP Documentation](https://invoiceninja.github.io/)
|
||||
* [Support Forum](https://forum.invoiceninja.com)
|
||||
* [StackOverflow](https://stackoverflow.com/tags/invoice-ninja/)
|
||||
|
||||
## Mobile Apps
|
||||
* [iPhone](https://apps.apple.com/app/id1503970375?platform=iphone)
|
||||
* [Android](https://play.google.com/store/apps/details?id=com.invoiceninja.app)
|
||||
|
||||
## Desktop Apps
|
||||
* [macOS](https://apps.apple.com/app/id1503970375?platform=mac)
|
||||
* [Windows](https://microsoft.com/en-us/p/invoice-ninja/9n3f2bbcfdr6)
|
||||
* [Linux](https://snapcraft.io/invoiceninja)
|
||||
|
||||
|
||||
## Installation Options
|
||||
* [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)
|
||||
|
||||
## Recommended Providers
|
||||
* [Stripe](https://stripe.com/)
|
||||
* [Postmark](https://postmarkapp.com/)
|
||||
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
## Credits
|
||||
* [Hillel Coren](https://hillelcoren.com/)
|
||||
* [David Bomba](https://github.com/turbo124)
|
||||
* [Benjamin Beganović](https://github.com/beganovich)
|
||||
* [All contributors](https://github.com/invoiceninja/invoiceninja/graphs/contributors)
|
||||
|
||||
## 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.
|
||||
|
|
|
|||
1
VERSION.txt
Normal file
1
VERSION.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
5.5.51
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Commands;
|
||||
|
||||
abstract class Command
|
||||
{
|
||||
}
|
||||
127
app/Console/Commands/BackupUpdate.php
Normal file
127
app/Console/Commands/BackupUpdate.php
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. 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\Backup;
|
||||
use App\Models\Company;
|
||||
use App\Models\Design;
|
||||
use App\Models\Document;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use stdClass;
|
||||
|
||||
class BackupUpdate extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:backup-files {--disk=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Shift files between object storage locations';
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
MultiDB::setDB($current_db);
|
||||
}
|
||||
}
|
||||
|
||||
private function handleOnDb()
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
||||
//logos
|
||||
Company::cursor()
|
||||
->each(function ($company){
|
||||
|
||||
$company_logo = $company->present()->logo();
|
||||
|
||||
if($company_logo == 'https://invoicing.co/images/new_logo.png')
|
||||
return;
|
||||
|
||||
$logo = @file_get_contents($company_logo);
|
||||
|
||||
if($logo){
|
||||
|
||||
$path = str_replace("https://objects.invoicing.co/", "", $company->present()->logo());
|
||||
$path = str_replace("https://v5-at-backup.us-southeast-1.linodeobjects.com/", "", $path);
|
||||
|
||||
Storage::disk($this->option('disk'))->put($path, $logo);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
//documents
|
||||
Document::cursor()
|
||||
->each(function ($document){
|
||||
|
||||
$doc_bin = $document->getFile();
|
||||
|
||||
if($doc_bin)
|
||||
Storage::disk($this->option('disk'))->put($document->url, $doc_bin);
|
||||
|
||||
});
|
||||
|
||||
|
||||
//backups
|
||||
Backup::cursor()
|
||||
->each(function ($backup){
|
||||
|
||||
$backup_bin = Storage::disk('s3')->get($backup->filename);
|
||||
|
||||
if($backup_bin)
|
||||
Storage::disk($this->option('disk'))->put($backup->filename, $backup_bin);
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
<?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],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
<?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
152
app/Console/Commands/CheckDb.php
Normal file
152
app/Console/Commands/CheckDb.php
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. 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\Paymentable;
|
||||
use App\Models\PaymentHash;
|
||||
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
|
||||
{
|
||||
protected $signature = 'ninja:check-db';
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
166
app/Console/Commands/CreateAccount.php
Normal file
166
app/Console/Commands/CreateAccount.php
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. 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,
|
||||
]);
|
||||
|
||||
(new CreateCompanyPaymentTerms($company, $user))->handle();
|
||||
(new CreateCompanyTaskStatuses($company, $user))->handle();
|
||||
(new VersionCheck())->handle();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,223 +0,0 @@
|
|||
<?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 [];
|
||||
}
|
||||
}
|
||||
874
app/Console/Commands/CreateSingleAccount.php
Normal file
874
app/Console/Commands/CreateSingleAccount.php
Normal file
|
|
@ -0,0 +1,874 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\DataMapper\ClientRegistrationFields;
|
||||
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\Libraries\MultiDB;
|
||||
use App\Models\Account;
|
||||
use App\Models\BankIntegration;
|
||||
use App\Models\BankTransaction;
|
||||
use App\Models\BankTransactionRule;
|
||||
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 Database\Factories\BankTransactionRuleFactory;
|
||||
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;
|
||||
|
||||
protected $description = 'Create Single Sample Account';
|
||||
|
||||
protected $signature = 'ninja:create-single-account {gateway=all} {--database=db-ninja-01}';
|
||||
|
||||
protected $invoice_repo;
|
||||
|
||||
protected $count;
|
||||
|
||||
protected $gateway;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
if(config('ninja.is_docker'))
|
||||
return;
|
||||
|
||||
if (!$this->confirm('Are you sure you want to inject dummy data?'))
|
||||
return;
|
||||
|
||||
$this->invoice_repo = new InvoiceRepository();
|
||||
|
||||
MultiDB::setDb($this->option('database'));
|
||||
|
||||
$this->info(date('r').' Create Single Sample Account...');
|
||||
$this->count = 5;
|
||||
$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',
|
||||
'track_inventory' => true
|
||||
]);
|
||||
|
||||
$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->client_registration_fields = ClientRegistrationFields::generate();
|
||||
$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
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'company_id' => $company->id,
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
BankTransaction::factory()->count(50)->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
]);
|
||||
|
||||
$btr = BankTransactionRule::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'applies_to' => (bool)rand(0,1) ? 'CREDIT' : 'DEBIT',
|
||||
]);
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
(new CreateCompanyTaskStatuses($company, $user))->handle();
|
||||
|
||||
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' => [config('ninja.ninja_hosted_header') => config('ninja.ninja_hosted_secret')],
|
||||
];
|
||||
|
||||
$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, 20))->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,
|
||||
'client_id' => $client->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) {
|
||||
// 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()->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()->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()->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()->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()->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()->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.square') && ($this->gateway == 'all' || $this->gateway == 'square')) {
|
||||
$cg = new CompanyGateway;
|
||||
$cg->company_id = $company->id;
|
||||
$cg->user_id = $user->id;
|
||||
$cg->gateway_key = '65faab2ab6e3223dbe848b1686490baz';
|
||||
$cg->require_cvv = true;
|
||||
$cg->require_billing_address = true;
|
||||
$cg->require_shipping_address = true;
|
||||
$cg->update_details = true;
|
||||
$cg->config = encrypt(config('ninja.testvars.square'));
|
||||
$cg->save();
|
||||
|
||||
$gateway_types = $cg->driver()->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()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,314 +1,701 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
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\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\Models\Client;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\Country;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Document;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Product;
|
||||
use App\Models\Project;
|
||||
use App\Models\ExpenseCategory;
|
||||
use Auth;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
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 Utils;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class CreateTestData.
|
||||
*/
|
||||
class CreateTestData extends Command
|
||||
{
|
||||
use MakesHash, GeneratesCounter;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create Test Data';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:create-test-data {count=1} {create_account=false} {--database}';
|
||||
protected $signature = 'ninja:create-test-data {count=1}';
|
||||
|
||||
protected $invoice_repo;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
protected $token;
|
||||
|
||||
/**
|
||||
* CreateTestData constructor.
|
||||
* Execute the console command.
|
||||
*
|
||||
* @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->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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (Utils::isNinjaProd()) {
|
||||
$this->info('Unable to run in production');
|
||||
return false;
|
||||
if (config('ninja.is_docker')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->confirm('Are you sure you want to inject dummy data?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->invoice_repo = new InvoiceRepository();
|
||||
|
||||
$this->info(date('r').' Running CreateTestData...');
|
||||
$this->count = $this->argument('count');
|
||||
|
||||
if ($database = $this->option('database')) {
|
||||
config(['database.default' => $database]);
|
||||
}
|
||||
$this->info('Warming up cache');
|
||||
|
||||
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->warmCache();
|
||||
|
||||
$this->createClients();
|
||||
$this->createVendors();
|
||||
$this->createOtherObjects();
|
||||
|
||||
$this->info('Done');
|
||||
$this->createSmallAccount();
|
||||
$this->createMediumAccount();
|
||||
$this->createLargeAccount();
|
||||
}
|
||||
|
||||
private function createClients()
|
||||
private function createSmallAccount()
|
||||
{
|
||||
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,
|
||||
]],
|
||||
];
|
||||
$this->info('Creating Small Account and Company');
|
||||
|
||||
$client = $this->clientRepo->save($data);
|
||||
$this->info('Client: ' . $client->name);
|
||||
$account = Account::factory()->create();
|
||||
$company = Company::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'slack_webhook_url' => config('ninja.notification.slack'),
|
||||
]);
|
||||
|
||||
$this->createInvoices($client);
|
||||
$this->createInvoices($client, true);
|
||||
// $this->createTasks($client);
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $client
|
||||
*/
|
||||
private function createInvoices($client, $isQuote = false)
|
||||
private function createMediumAccount()
|
||||
{
|
||||
for ($i = 0; $i < $this->count; $i++) {
|
||||
$data = [
|
||||
'is_public' => true,
|
||||
'is_quote' => $isQuote,
|
||||
'client_id' => $client->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)),
|
||||
]],
|
||||
];
|
||||
$this->info('Creating Medium Account and Company');
|
||||
|
||||
$invoice = $this->invoiceRepo->save($data);
|
||||
$this->info('Invoice: ' . $invoice->invoice_number);
|
||||
$account = Account::factory()->create();
|
||||
$company = Company::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'slack_webhook_url' => config('ninja.notification.slack'),
|
||||
]);
|
||||
|
||||
if (! $isQuote) {
|
||||
$this->createPayment($client, $invoice);
|
||||
$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,
|
||||
]);
|
||||
|
||||
Document::factory()->count(50)->create([
|
||||
'user_id' => $user->id,
|
||||
'company_id' => $company->id,
|
||||
'documentable_type' => Client::class,
|
||||
'documentable_id' => $client->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,
|
||||
'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,
|
||||
]);
|
||||
|
||||
Document::factory()->count(5)->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company_id,
|
||||
'documentable_type' => Vendor::class,
|
||||
'documentable_id' => $vendor->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,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createTask($client)
|
||||
{
|
||||
$vendor = Task::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company->id,
|
||||
]);
|
||||
|
||||
Document::factory()->count(5)->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company_id,
|
||||
'documentable_type' => Task::class,
|
||||
'documentable_id' => $vendor->id,
|
||||
]);
|
||||
}
|
||||
|
||||
private function createProject($client)
|
||||
{
|
||||
$vendor = Project::factory()->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company->id,
|
||||
]);
|
||||
|
||||
Document::factory()->count(5)->create([
|
||||
'user_id' => $client->user->id,
|
||||
'company_id' => $client->company_id,
|
||||
'documentable_type' => Project::class,
|
||||
'documentable_id' => $vendor->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;
|
||||
// $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();
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$this->invoice_repo->markSent($invoice);
|
||||
}
|
||||
|
||||
if (rand(0, 1)) {
|
||||
$invoice = $invoice->service()->markPaid()->save();
|
||||
}
|
||||
|
||||
Document::factory()->count(5)->create([
|
||||
'user_id' => $invoice->user->id,
|
||||
'company_id' => $invoice->company_id,
|
||||
'documentable_type' => Invoice::class,
|
||||
'documentable_id' => $invoice->id,
|
||||
]);
|
||||
|
||||
RecurringInvoice::factory()->create(['user_id' => $invoice->user->id, 'company_id' => $invoice->company->id, 'client_id' => $invoice->client_id]);
|
||||
|
||||
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->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();
|
||||
}
|
||||
|
||||
private function createQuote($client)
|
||||
{
|
||||
$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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $client
|
||||
* @param $invoice
|
||||
*/
|
||||
private function createPayment($client, $invoice)
|
||||
{
|
||||
$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'),
|
||||
];
|
||||
|
||||
$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,
|
||||
'amount' => $this->faker->randomFloat(2, 1, 10),
|
||||
'expense_date' => date_create()->modify(rand(-100, 100) . ' days')->format('Y-m-d'),
|
||||
'public_notes' => '',
|
||||
];
|
||||
|
||||
$expense = $this->expenseRepo->save($data);
|
||||
$this->info('Expense: ' . $expense->amount);
|
||||
}
|
||||
}
|
||||
|
||||
private function createOtherObjects()
|
||||
{
|
||||
$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 createTaxRate($name, $rate, $publicId)
|
||||
{
|
||||
$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 createCategory($name, $publicId)
|
||||
{
|
||||
$category = new ExpenseCategory();
|
||||
$category->name = $name;
|
||||
$category->account_id = 1;
|
||||
$category->user_id = 1;
|
||||
$category->public_id = $publicId;
|
||||
$category->save();
|
||||
}
|
||||
|
||||
private function createProject($name, $publicId)
|
||||
{
|
||||
$project = new Project();
|
||||
$project->name = $name;
|
||||
$project->account_id = 1;
|
||||
$project->client_id = 1;
|
||||
$project->user_id = 1;
|
||||
$project->public_id = $publicId;
|
||||
$project->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getArguments()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getOptions()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
649
app/Console/Commands/DemoMode.php
Normal file
649
app/Console/Commands/DemoMode.php
Normal file
|
|
@ -0,0 +1,649 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. 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\BankIntegration;
|
||||
use App\Models\BankTransaction;
|
||||
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\AppSetup;
|
||||
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, AppSetup;
|
||||
|
||||
protected $signature = 'ninja:demo-mode';
|
||||
|
||||
protected $description = 'Setup demo mode';
|
||||
|
||||
protected $invoice_repo;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
||||
if (config('ninja.is_docker')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->invoice_repo = new InvoiceRepository();
|
||||
|
||||
$cached_tables = config('ninja.cached_tables');
|
||||
|
||||
$this->info('Migrating');
|
||||
Artisan::call('migrate:fresh --force');
|
||||
|
||||
$this->info('Seeding');
|
||||
Artisan::call('db:seed --force');
|
||||
|
||||
$this->buildCache(true);
|
||||
|
||||
$this->info('Seeding Random Data');
|
||||
$this->createSmallAccount();
|
||||
|
||||
(new VersionCheck())->handle();
|
||||
|
||||
(new CompanySizeCheck())->handle();
|
||||
}
|
||||
|
||||
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,
|
||||
'markdown_email_enabled' => true,
|
||||
'markdown_enabled' => false,
|
||||
]);
|
||||
|
||||
$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(),
|
||||
]);
|
||||
}
|
||||
|
||||
(new CreateCompanyPaymentTerms($company, $user))->handle();
|
||||
(new CreateCompanyTaskStatuses($company, $user))->handle();
|
||||
|
||||
$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,
|
||||
]);
|
||||
|
||||
$bi = BankIntegration::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'company_id' => $company->id,
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
BankTransaction::factory()->count(50)->create([
|
||||
'bank_integration_id' => $bi->id,
|
||||
'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);
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
89
app/Console/Commands/DesignUpdate.php
Normal file
89
app/Console/Commands/DesignUpdate.php
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
<?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.');
|
||||
}
|
||||
}
|
||||
125
app/Console/Commands/HostedMigrations.php
Normal file
125
app/Console/Commands/HostedMigrations.php
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. 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\Libraries\MultiDB;
|
||||
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 HostedMigrations extends Command
|
||||
{
|
||||
use MakesHash;
|
||||
use AppSetup;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:import {--email=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Import a v4 migration file';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->buildCache();
|
||||
|
||||
if (! MultiDB::userFindAndSetDb($this->option('email'))) {
|
||||
$this->info('Could not find a user with that email address');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$user = User::where('email', $this->option('email'))->first();
|
||||
|
||||
if (! $user) {
|
||||
$this->info('There was a problem getting the user, did you set the right DB?');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$path = public_path('storage/migrations/import');
|
||||
|
||||
$directory = new DirectoryIterator($path);
|
||||
|
||||
foreach ($directory as $file) {
|
||||
if ($file->getExtension() === 'zip') {
|
||||
$company = $user->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, $user->companies()->first(), $user);
|
||||
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) {
|
||||
\Mail::to($this->user)->send(new MigrationFailed($e, $e->getMessage()));
|
||||
|
||||
if (app()->environment() !== 'production') {
|
||||
info($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
56
app/Console/Commands/HostedUsers.php
Normal file
56
app/Console/Commands/HostedUsers.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?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()) {
|
||||
(new \Modules\Admin\Jobs\Account\NinjaUser([], $company))->handle();
|
||||
}
|
||||
});
|
||||
|
||||
Company::on('db-ninja-02')->each(function ($company) {
|
||||
if (Ninja::isHosted()) {
|
||||
(new \Modules\Admin\Jobs\Account\NinjaUser([], $company))->handle();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
173
app/Console/Commands/ImportMigrations.php
Normal file
173
app/Console/Commands/ImportMigrations.php
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. 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 = 'ninja:old-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');
|
||||
|
||||
$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($company->db),
|
||||
]);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,380 +0,0 @@
|
|||
<?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],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
<?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],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -2,11 +2,10 @@
|
|||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\DbServer;
|
||||
use App\Models\User;
|
||||
use App\Models\Company;
|
||||
use App\Libraries\CurlUtils;
|
||||
use App\Models\User;
|
||||
use App\Utils\CurlUtils;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class MobileLocalization extends Command
|
||||
{
|
||||
|
|
@ -24,7 +23,6 @@ class MobileLocalization extends Command
|
|||
*/
|
||||
protected $description = 'Generate mobile localization resources';
|
||||
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
|
|
@ -84,9 +82,9 @@ class MobileLocalization extends Command
|
|||
$text = $resources->$key;
|
||||
}
|
||||
|
||||
$text = str_replace(array('<b>', '</b>'), '', $text);
|
||||
$text = str_replace(array('<i>', '</i>'), '', $text);
|
||||
$text = str_replace(array('<strong>', '</strong>'), '', $text);
|
||||
$text = str_replace(['<b>', '</b>'], '', $text);
|
||||
$text = str_replace(['<i>', '</i>'], '', $text);
|
||||
$text = str_replace(['<strong>', '</strong>'], '', $text);
|
||||
|
||||
echo "'$key': '$text',\n";
|
||||
}
|
||||
|
|
@ -104,11 +102,12 @@ class MobileLocalization extends Command
|
|||
$end = strpos($data, '},', $start);
|
||||
$data = substr($data, $start, $end - $start - 5);
|
||||
|
||||
$data = str_replace("\n", "", $data);
|
||||
$data = str_replace("\"", "\'", $data);
|
||||
$data = str_replace("'", "\"", $data);
|
||||
$data = str_replace("\n", '', $data);
|
||||
$data = str_replace("\'", "\#", $data);
|
||||
$data = str_replace("'", '"', $data);
|
||||
$data = str_replace("\#", "'", $data);
|
||||
|
||||
return json_decode('{' . rtrim($data, ',') . '}');
|
||||
return json_decode('{'.rtrim($data, ',').'}');
|
||||
}
|
||||
|
||||
protected function getOptions()
|
||||
|
|
@ -117,5 +116,4 @@ class MobileLocalization extends Command
|
|||
['type', null, InputOption::VALUE_OPTIONAL, 'Type', null],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
61
app/Console/Commands/ParallelCheckData.php
Normal file
61
app/Console/Commands/ParallelCheckData.php
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. 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 $signature = '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');
|
||||
});
|
||||
}
|
||||
}
|
||||
90
app/Console/Commands/PostUpdate.php
Normal file
90
app/Console/Commands/PostUpdate.php
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Jobs\Util\VersionCheck;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
class PostUpdate extends Command
|
||||
{
|
||||
/**
|
||||
* 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.");
|
||||
}
|
||||
|
||||
info('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');
|
||||
|
||||
try {
|
||||
Artisan::call('queue:restart');
|
||||
} catch (\Exception $e) {
|
||||
info("I wasn't able to restart the queue.");
|
||||
}
|
||||
|
||||
info('queue restarted');
|
||||
|
||||
VersionCheck::dispatch();
|
||||
|
||||
info('Sent for version check');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
<?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],
|
||||
];
|
||||
}
|
||||
}
|
||||
74
app/Console/Commands/ReactBuilder.php
Normal file
74
app/Console/Commands/ReactBuilder.php
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. 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\Backup;
|
||||
use App\Models\Design;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use stdClass;
|
||||
|
||||
class ReactBuilder extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:react';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Builds blade component for react includes';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$includes = '';
|
||||
|
||||
$directoryIterator = new \RecursiveDirectoryIterator(public_path('react'), \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
|
||||
foreach (new \RecursiveIteratorIterator($directoryIterator) as $file) {
|
||||
if (str_contains($file->getFileName(), '.js')) {
|
||||
if (str_contains($file->getFileName(), 'index.')) {
|
||||
$includes .= '<script type="module" crossorigin src="/react/'.$file->getFileName().'"></script>'."\n";
|
||||
} else {
|
||||
$includes .= '<link rel="modulepreload" href="/react/'.$file->getFileName().'">'."\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (str_contains($file->getFileName(), '.css')) {
|
||||
$includes .= '<link rel="stylesheet" href="/react/'.$file->getFileName().'">'."\n";
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents(resource_path('views/react/head.blade.php'), $includes);
|
||||
}
|
||||
}
|
||||
52
app/Console/Commands/RecurringCommand.php
Normal file
52
app/Console/Commands/RecurringCommand.php
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. 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()
|
||||
{
|
||||
(new RecurringInvoicesCron())->handle();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
<?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],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
<?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],
|
||||
];
|
||||
}
|
||||
}
|
||||
78
app/Console/Commands/S3Cleanup.php
Normal file
78
app/Console/Commands/S3Cleanup.php
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Utils\Ninja;
|
||||
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';
|
||||
|
||||
protected $log = '';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Remove orphan folders/files';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (!Ninja::isHosted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$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");
|
||||
|
||||
/* Ensure we are not deleting the root folder */
|
||||
if (strlen($dir) > 1) {
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
<?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],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,280 +0,0 @@
|
|||
<?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],
|
||||
];
|
||||
}
|
||||
}
|
||||
231
app/Console/Commands/SendRemindersCron.php
Normal file
231
app/Console/Commands/SendRemindersCron.php
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\DataMapper\InvoiceItem;
|
||||
use App\Events\Invoice\InvoiceWasEmailed;
|
||||
use App\Jobs\Entity\EmailEntity;
|
||||
use App\Jobs\Ninja\SendReminders;
|
||||
use App\Jobs\Util\WebhookHandler;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Quote;
|
||||
use App\Models\Webhook;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesDates;
|
||||
use App\Utils\Traits\MakesReminders;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
//@deprecated 27-11-2022 - only ever should be used for testing
|
||||
class SendRemindersCron extends Command
|
||||
{
|
||||
use MakesReminders, MakesDates;
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
Invoice::where('next_send_date', '<=', now()->toDateTimeString())
|
||||
->whereNull('deleted_at')
|
||||
->where('is_deleted', 0)
|
||||
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
|
||||
->where('balance', '>', 0)
|
||||
->whereHas('client', function ($query) {
|
||||
$query->where('is_deleted', 0)
|
||||
->where('deleted_at', null);
|
||||
})
|
||||
->whereHas('company', function ($query) {
|
||||
$query->where('is_disabled', 0);
|
||||
})
|
||||
->with('invitations')->cursor()->each(function ($invoice) {
|
||||
if ($invoice->isPayable()) {
|
||||
$reminder_template = $invoice->calculateTemplate('invoice');
|
||||
$invoice->service()->touchReminder($reminder_template)->save();
|
||||
$invoice = $this->calcLateFee($invoice, $reminder_template);
|
||||
|
||||
//check if this reminder needs to be emailed
|
||||
if (in_array($reminder_template, ['reminder1', 'reminder2', 'reminder3']) && $invoice->client->getSetting('enable_'.$reminder_template)) {
|
||||
$invoice->invitations->each(function ($invitation) use ($invoice, $reminder_template) {
|
||||
EmailEntity::dispatch($invitation, $invitation->company, $reminder_template);
|
||||
nlog("Firing reminder email for invoice {$invoice->number}");
|
||||
});
|
||||
|
||||
if ($invoice->invitations->count() > 0) {
|
||||
event(new InvoiceWasEmailed($invoice->invitations->first(), $invoice->company, Ninja::eventVars(), $reminder_template));
|
||||
}
|
||||
}
|
||||
$invoice->service()->setReminder()->save();
|
||||
} else {
|
||||
$invoice->next_send_date = null;
|
||||
$invoice->save();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private function calcLateFee($invoice, $template) :Invoice
|
||||
{
|
||||
$late_fee_amount = 0;
|
||||
$late_fee_percent = 0;
|
||||
|
||||
switch ($template) {
|
||||
case 'reminder1':
|
||||
$late_fee_amount = $invoice->client->getSetting('late_fee_amount1');
|
||||
$late_fee_percent = $invoice->client->getSetting('late_fee_percent1');
|
||||
break;
|
||||
case 'reminder2':
|
||||
$late_fee_amount = $invoice->client->getSetting('late_fee_amount2');
|
||||
$late_fee_percent = $invoice->client->getSetting('late_fee_percent2');
|
||||
break;
|
||||
case 'reminder3':
|
||||
$late_fee_amount = $invoice->client->getSetting('late_fee_amount3');
|
||||
$late_fee_percent = $invoice->client->getSetting('late_fee_percent3');
|
||||
break;
|
||||
case 'endless_reminder':
|
||||
$late_fee_amount = $invoice->client->getSetting('late_fee_endless_amount');
|
||||
$late_fee_percent = $invoice->client->getSetting('late_fee_endless_percent');
|
||||
break;
|
||||
default:
|
||||
$late_fee_amount = 0;
|
||||
$late_fee_percent = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->setLateFee($invoice, $late_fee_amount, $late_fee_percent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the late fee to the invoice line items
|
||||
*
|
||||
* @param Invoice $invoice
|
||||
* @param float $amount The fee amount
|
||||
* @param float $percent The fee percentage amount
|
||||
*
|
||||
* @return Invoice
|
||||
*/
|
||||
private function setLateFee($invoice, $amount, $percent) :Invoice
|
||||
{
|
||||
App::forgetInstance('translator');
|
||||
$t = app('translator');
|
||||
$t->replace(Ninja::transformTranslations($invoice->client->getMergedSettings()));
|
||||
App::setLocale($invoice->client->locale());
|
||||
|
||||
$temp_invoice_balance = $invoice->balance;
|
||||
|
||||
if ($amount <= 0 && $percent <= 0) {
|
||||
return $invoice;
|
||||
}
|
||||
|
||||
$fee = $amount;
|
||||
|
||||
if ($invoice->partial > 0) {
|
||||
$fee += round($invoice->partial * $percent / 100, 2);
|
||||
} else {
|
||||
$fee += round($invoice->balance * $percent / 100, 2);
|
||||
}
|
||||
|
||||
$invoice_item = new InvoiceItem;
|
||||
$invoice_item->type_id = '5';
|
||||
$invoice_item->product_key = ctrans('texts.fee');
|
||||
$invoice_item->notes = ctrans('texts.late_fee_added', ['date' => $this->translateDate(now()->startOfDay(), $invoice->client->date_format(), $invoice->client->locale())]);
|
||||
$invoice_item->quantity = 1;
|
||||
$invoice_item->cost = $fee;
|
||||
|
||||
$invoice_items = $invoice->line_items;
|
||||
$invoice_items[] = $invoice_item;
|
||||
|
||||
$invoice->line_items = $invoice_items;
|
||||
|
||||
/**Refresh Invoice values*/
|
||||
$invoice->calc()->getInvoice()->save();
|
||||
$invoice->fresh();
|
||||
$invoice->service()->deletePdf()->save();
|
||||
|
||||
/* Refresh the client here to ensure the balance is fresh */
|
||||
$client = $invoice->client;
|
||||
$client = $client->fresh();
|
||||
|
||||
nlog('adjusting client balance and invoice balance by '.($invoice->balance - $temp_invoice_balance));
|
||||
$client->service()->updateBalance($invoice->balance - $temp_invoice_balance)->save();
|
||||
$invoice->ledger()->updateInvoiceBalance($invoice->balance - $temp_invoice_balance, "Late Fee Adjustment for invoice {$invoice->number}");
|
||||
|
||||
return $invoice;
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
<?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],
|
||||
];
|
||||
}
|
||||
}
|
||||
101
app/Console/Commands/SendTestEmails.php
Normal file
101
app/Console/Commands/SendTestEmails.php
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. 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;
|
||||
|
||||
(new NinjaMailerJob($nmo))->handle();
|
||||
}
|
||||
}
|
||||
56
app/Console/Commands/SubdomainFill.php
Normal file
56
app/Console/Commands/SubdomainFill.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
<?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...');
|
||||
}
|
||||
}
|
||||
160
app/Console/Commands/TranslationsExport.php
Normal file
160
app/Console/Commands/TranslationsExport.php
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. 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\Backup;
|
||||
use App\Models\Design;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Lang;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use stdClass;
|
||||
|
||||
class TranslationsExport extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:translations {--type=} {--path=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Transform translations to json';
|
||||
|
||||
protected $log = '';
|
||||
|
||||
private array $langs = [
|
||||
'ar',
|
||||
'bg',
|
||||
'ca',
|
||||
'cs',
|
||||
'da',
|
||||
'de',
|
||||
'el',
|
||||
'en',
|
||||
'en_GB',
|
||||
'es',
|
||||
'es_ES',
|
||||
'et',
|
||||
'fa',
|
||||
'fi',
|
||||
'fr',
|
||||
'fr_CA',
|
||||
'he',
|
||||
'hr',
|
||||
'it',
|
||||
'ja',
|
||||
'lt',
|
||||
'lv_LV',
|
||||
'mk_MK',
|
||||
'nb_NO',
|
||||
'nl',
|
||||
'pl',
|
||||
'pt_BR',
|
||||
'pt_PT',
|
||||
'ro',
|
||||
'ru_RU',
|
||||
'sl',
|
||||
'sk',
|
||||
'sq',
|
||||
'sr',
|
||||
'sv',
|
||||
'th',
|
||||
'tr_TR',
|
||||
'zh_TW',
|
||||
];
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$type =$this->option('type') ?? 'export';
|
||||
|
||||
if($type == 'import')
|
||||
$this->import();
|
||||
|
||||
if($type == 'export')
|
||||
$this->export();
|
||||
|
||||
}
|
||||
|
||||
private function import()
|
||||
{
|
||||
//loop and
|
||||
|
||||
foreach($this->langs as $lang)
|
||||
{
|
||||
|
||||
$import_file = "textsphp_{$lang}.php";
|
||||
$dir = $this->option('path') ?? storage_path('lang_import/');
|
||||
$path = $dir.$import_file;
|
||||
|
||||
if(file_exists($path)){
|
||||
$this->logMessage($path);
|
||||
|
||||
$trans = file_get_contents($path);
|
||||
|
||||
file_put_contents(lang_path("/{$lang}/texts.php"), $trans);
|
||||
|
||||
}
|
||||
else{
|
||||
|
||||
$this->logMessage("Could not open file");
|
||||
$this->logMessage($path);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private function export()
|
||||
{
|
||||
Storage::disk('local')->makeDirectory('lang');
|
||||
|
||||
foreach ($this->langs as $lang) {
|
||||
Storage::disk('local')->makeDirectory("lang/{$lang}");
|
||||
|
||||
$translations = Lang::getLoader()->load($lang, 'texts');
|
||||
|
||||
Storage::disk('local')->put("lang/{$lang}/{$lang}.json", json_encode(Arr::dot($translations), JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
|
||||
private function logMessage($str)
|
||||
{
|
||||
$str = date('Y-m-d h:i:s').' '.$str;
|
||||
$this->info($str);
|
||||
$this->log .= $str."\n";
|
||||
}
|
||||
|
||||
}
|
||||
143
app/Console/Commands/TypeCheck.php
Normal file
143
app/Console/Commands/TypeCheck.php
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Http\ValidationRules\ValidClientGroupSettingsRule;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Backup;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\Design;
|
||||
use App\Utils\Traits\ClientGroupSettingsSaver;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use stdClass;
|
||||
|
||||
class TypeCheck extends Command
|
||||
{
|
||||
use ClientGroupSettingsSaver;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'ninja:type-check {--all=} {--client_id=} {--company_id=}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Check Settings Types';
|
||||
|
||||
protected $log = '';
|
||||
|
||||
/**
|
||||
* 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 ($this->option('all')) {
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
$this->checkAll();
|
||||
} else {
|
||||
foreach (MultiDB::$dbs as $db) {
|
||||
MultiDB::setDB($db);
|
||||
|
||||
$this->checkAll();
|
||||
}
|
||||
|
||||
MultiDB::setDB($current_db);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->option('client_id')) {
|
||||
$client = MultiDB::findAndSetDbByClientId($this->option('client_id'));
|
||||
|
||||
if ($client) {
|
||||
$this->checkClient($client);
|
||||
} else {
|
||||
$this->logMessage(date('Y-m-d h:i:s').' Could not find this client');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->option('company_id')) {
|
||||
$company = MultiDB::findAndSetDbByCompanyId($this->option('company_id'));
|
||||
|
||||
if ($company) {
|
||||
$this->checkCompany($company);
|
||||
} else {
|
||||
$this->logMessage(date('Y-m-d h:i:s').' Could not find this company');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function checkClient($client)
|
||||
{
|
||||
$this->logMessage(date('Y-m-d h:i:s').' Checking Client => '.$client->present()->name().' '.$client->id);
|
||||
|
||||
$entity_settings = $this->checkSettingType($client->settings);
|
||||
$entity_settings->md5 = md5(time());
|
||||
$client->settings = $entity_settings;
|
||||
$client->save();
|
||||
}
|
||||
|
||||
private function checkCompany($company)
|
||||
{
|
||||
$this->logMessage(date('Y-m-d h:i:s').' Checking Company => '.$company->present()->name().' '.$company->id);
|
||||
|
||||
$company->saveSettings((array) $company->settings, $company);
|
||||
}
|
||||
|
||||
private function checkAll()
|
||||
{
|
||||
$this->logMessage(date('Y-m-d h:i:s').' Checking all clients and companies.');
|
||||
|
||||
Client::withTrashed()->cursor()->each(function ($client) {
|
||||
$this->logMessage("Checking client {$client->id}");
|
||||
$entity_settings = $this->checkSettingType($client->settings);
|
||||
$entity_settings->md5 = md5(time());
|
||||
$client->settings = $entity_settings;
|
||||
$client->save();
|
||||
});
|
||||
|
||||
Company::cursor()->each(function ($company) {
|
||||
$this->logMessage("Checking company {$company->id}");
|
||||
$company->saveSettings($company->settings, $company);
|
||||
});
|
||||
}
|
||||
|
||||
private function logMessage($str)
|
||||
{
|
||||
$str = date('Y-m-d h:i:s').' '.$str;
|
||||
$this->info($str);
|
||||
$this->log .= $str."\n";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
<?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],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
<?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$);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<?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,
|
||||
];
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
<?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],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"name": "$VENDOR$/$LOWER_NAME$",
|
||||
"description": "",
|
||||
"authors": [
|
||||
{
|
||||
"name": "$AUTHOR_NAME$",
|
||||
"email": "$AUTHOR_EMAIL$"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"$MODULE_NAMESPACE$\\$STUDLY_NAME$\\": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $CLASS_NAMESPACE$;
|
||||
|
||||
use Illuminate\Routing\Controller;
|
||||
|
||||
class $CLASS$ extends Controller
|
||||
{
|
||||
}
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
<?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'));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<?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 [
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
<?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]);
|
||||
}
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
<?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 [];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<?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()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"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$"
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?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;
|
||||
|
||||
?>
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<?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)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<?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');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<?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$
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
<?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$'));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<?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$
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<?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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<?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()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
<?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$';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
<?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 [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use App\Policies\EntityPolicy;
|
||||
|
||||
class $STUDLY_NAME$Policy extends EntityPolicy
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use App\Ninja\Presenters\EntityPresenter;
|
||||
|
||||
class $STUDLY_NAME$Presenter extends EntityPresenter
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
<?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 [];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
<?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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace $NAMESPACE$;
|
||||
|
||||
use App\Http\Requests\EntityRequest;
|
||||
|
||||
class $CLASS$Request extends EntityRequest
|
||||
{
|
||||
protected $entityType = '$LOWER_NAME$';
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
<?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';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?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');
|
||||
});
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'name' => '$STUDLY_NAME$'
|
||||
];
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
<?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 [];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?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");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<?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';
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<?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),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<?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 [
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
@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
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue