commit 74f95a2f9162c7da46f06e263b4f47902649d135 Author: Brandon Shipley Date: Sat Aug 9 14:07:39 2025 -0700 first commit wtf lol diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f2f952a --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +/composer.lock +/composer.phar +/phpunit.xml +/.phpunit.result.cache +/.phpunit.cache +/phpunit.phar +/config/Migrations/schema-dump-default.lock +/vendor/ +/.idea/ +config/app_local.php \ No newline at end of file diff --git a/.idea/CakeAccounting.iml b/.idea/CakeAccounting.iml new file mode 100644 index 0000000..c956989 --- /dev/null +++ b/.idea/CakeAccounting.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..a1511bd --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2a747a8 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# CakeAccounting plugin for CakePHP + +## Installation + +You can install this plugin into your CakePHP application using [composer](https://getcomposer.org). + +The recommended way to install composer packages is: + +``` +composer require your-name-here/cake-accounting +``` diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..f79f33f --- /dev/null +++ b/composer.json @@ -0,0 +1,35 @@ +{ + "name": "hi-powered-dev/cake-accounting", + "description": "CakeAccounting plugin for CakePHP", + "type": "cakephp-plugin", + "license": "AGPL-3.0-or-later", + "keywords": [ + "cakephp" + ], + "authors": [ + { + "name": "HiPoweredDev", + "homepage": "https://hipowered.dev", + "role": "Author" + } + ], + "require": { + "php": ">=8.1", + "cakephp/cakephp": "^5.0.1", + "ext-bcmath" : "*" + }, + "require-dev": { + "phpunit/phpunit": "^10.1" + }, + "autoload": { + "psr-4": { + "CakeAccounting\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "CakeAccounting\\Test\\": "tests/", + "Cake\\Test\\": "vendor/cakephp/cakephp/tests/" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..cf345f3 --- /dev/null +++ b/composer.lock @@ -0,0 +1,2613 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "5ff8ff9e9e8e285405906c0121800ee0", + "packages": [ + { + "name": "cakephp/cakephp", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/cakephp/cakephp.git", + "reference": "b7ac799b88cd751cdbe8ce808767a0e8fbf474bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/cakephp/zipball/b7ac799b88cd751cdbe8ce808767a0e8fbf474bb", + "reference": "b7ac799b88cd751cdbe8ce808767a0e8fbf474bb", + "shasum": "" + }, + "require": { + "cakephp/chronos": "^3.1", + "composer/ca-bundle": "^1.5", + "ext-intl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "laminas/laminas-diactoros": "^3.3", + "laminas/laminas-httphandlerrunner": "^2.6", + "league/container": "^4.2", + "php": ">=8.1", + "psr/container": "^1.1 || ^2.0", + "psr/http-client": "^1.0.2", + "psr/http-factory": "^1.1", + "psr/http-message": "^1.1 || ^2.0", + "psr/http-server-handler": "^1.0.2", + "psr/http-server-middleware": "^1.0.2", + "psr/log": "^3.0", + "psr/simple-cache": "^2.0 || ^3.0" + }, + "provide": { + "psr/container-implementation": "^2.0", + "psr/http-client-implementation": "^1.0", + "psr/http-factory-implementation": "^1.0", + "psr/http-server-handler-implementation": "^1.0", + "psr/http-server-middleware-implementation": "^1.0", + "psr/log-implementation": "^3.0", + "psr/simple-cache-implementation": "^3.0" + }, + "replace": { + "cakephp/cache": "self.version", + "cakephp/collection": "self.version", + "cakephp/console": "self.version", + "cakephp/core": "self.version", + "cakephp/database": "self.version", + "cakephp/datasource": "self.version", + "cakephp/event": "self.version", + "cakephp/form": "self.version", + "cakephp/http": "self.version", + "cakephp/i18n": "self.version", + "cakephp/log": "self.version", + "cakephp/orm": "self.version", + "cakephp/utility": "self.version", + "cakephp/validation": "self.version" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "^5.0", + "http-interop/http-factory-tests": "^2.0", + "mikey179/vfsstream": "^1.6.10", + "mockery/mockery": "^1.6", + "paragonie/csp-builder": "^2.3 || ^3.0", + "phpstan/extension-installer": "^1.3", + "phpstan/phpstan": "1.12.3", + "phpunit/phpunit": "^10.5.5 || ^11.1.3", + "symplify/phpstan-rules": "^12.4" + }, + "suggest": { + "ext-curl": "To enable more efficient network calls in Http\\Client.", + "ext-openssl": "To use Security::encrypt() or have secure CSRF token generation.", + "lib-ICU": "To use locale-aware features in the I18n and Database packages", + "paragonie/csp-builder": "CSP builder, to use the CSP Middleware" + }, + "type": "library", + "autoload": { + "files": [ + "src/Core/functions.php", + "src/Error/functions.php", + "src/Collection/functions.php", + "src/I18n/functions.php", + "src/ORM/bootstrap.php", + "src/Routing/functions.php", + "src/Utility/bootstrap.php" + ], + "psr-4": { + "Cake\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/cakephp/graphs/contributors" + } + ], + "description": "The CakePHP framework", + "homepage": "https://cakephp.org", + "keywords": [ + "conventions over configuration", + "dry", + "form", + "framework", + "mvc", + "orm", + "psr-7", + "rapid-development", + "validation" + ], + "support": { + "forum": "https://discourse.cakephp.org/", + "issues": "https://github.com/cakephp/cakephp/issues", + "source": "https://github.com/cakephp/cakephp" + }, + "time": "2024-10-04T02:50:14+00:00" + }, + { + "name": "cakephp/chronos", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/cakephp/chronos.git", + "reference": "786d69e1ee4b735765cbdb5521b9603e9b98d650" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cakephp/chronos/zipball/786d69e1ee4b735765cbdb5521b9603e9b98d650", + "reference": "786d69e1ee4b735765cbdb5521b9603e9b98d650", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "cakephp/cakephp-codesniffer": "^5.0", + "phpunit/phpunit": "^10.1.0 || ^11.1.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Cake\\Chronos\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "http://nesbot.com" + }, + { + "name": "The CakePHP Team", + "homepage": "https://cakephp.org" + } + ], + "description": "A simple API extension for DateTime.", + "homepage": "https://cakephp.org", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "issues": "https://github.com/cakephp/chronos/issues", + "source": "https://github.com/cakephp/chronos" + }, + "time": "2024-07-18T03:18:04+00:00" + }, + { + "name": "composer/ca-bundle", + "version": "1.5.2", + "source": { + "type": "git", + "url": "https://github.com/composer/ca-bundle.git", + "reference": "48a792895a2b7a6ee65dd5442c299d7b835b6137" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/48a792895a2b7a6ee65dd5442c299d7b835b6137", + "reference": "48a792895a2b7a6ee65dd5442c299d7b835b6137", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "ext-pcre": "*", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8 || ^9", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "symfony/process": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\CaBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", + "keywords": [ + "cabundle", + "cacert", + "certificate", + "ssl", + "tls" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/ca-bundle/issues", + "source": "https://github.com/composer/ca-bundle/tree/1.5.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-09-25T07:49:53+00:00" + }, + { + "name": "laminas/laminas-diactoros", + "version": "3.5.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-diactoros.git", + "reference": "143a16306602ce56b8b092a7914fef03c37f9ed2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/143a16306602ce56b8b092a7914fef03c37f9ed2", + "reference": "143a16306602ce56b8b092a7914fef03c37f9ed2", + "shasum": "" + }, + "require": { + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "psr/http-factory": "^1.1", + "psr/http-message": "^1.1 || ^2.0" + }, + "conflict": { + "amphp/amp": "<2.6.4" + }, + "provide": { + "psr/http-factory-implementation": "^1.0", + "psr/http-message-implementation": "^1.1 || ^2.0" + }, + "require-dev": { + "ext-curl": "*", + "ext-dom": "*", + "ext-gd": "*", + "ext-libxml": "*", + "http-interop/http-factory-tests": "^2.2.0", + "laminas/laminas-coding-standard": "~2.5.0", + "php-http/psr7-integration-tests": "^1.4.0", + "phpunit/phpunit": "^10.5.36", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^5.26.1" + }, + "type": "library", + "extra": { + "laminas": { + "config-provider": "Laminas\\Diactoros\\ConfigProvider", + "module": "Laminas\\Diactoros" + } + }, + "autoload": { + "files": [ + "src/functions/create_uploaded_file.php", + "src/functions/marshal_headers_from_sapi.php", + "src/functions/marshal_method_from_sapi.php", + "src/functions/marshal_protocol_version_from_sapi.php", + "src/functions/normalize_server.php", + "src/functions/normalize_uploaded_files.php", + "src/functions/parse_cookie_header.php" + ], + "psr-4": { + "Laminas\\Diactoros\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "PSR HTTP Message implementations", + "homepage": "https://laminas.dev", + "keywords": [ + "http", + "laminas", + "psr", + "psr-17", + "psr-7" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-diactoros/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-diactoros/issues", + "rss": "https://github.com/laminas/laminas-diactoros/releases.atom", + "source": "https://github.com/laminas/laminas-diactoros" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2024-10-14T11:59:49+00:00" + }, + { + "name": "laminas/laminas-httphandlerrunner", + "version": "2.11.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-httphandlerrunner.git", + "reference": "c428d9f67f280d155637cbe2b7245b5188c8cdae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/c428d9f67f280d155637cbe2b7245b5188c8cdae", + "reference": "c428d9f67f280d155637cbe2b7245b5188c8cdae", + "shasum": "" + }, + "require": { + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "psr/http-message": "^1.0 || ^2.0", + "psr/http-message-implementation": "^1.0 || ^2.0", + "psr/http-server-handler": "^1.0" + }, + "require-dev": { + "laminas/laminas-coding-standard": "~3.0.0", + "laminas/laminas-diactoros": "^3.4.0", + "phpunit/phpunit": "^10.5.36", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^5.26.1" + }, + "type": "library", + "extra": { + "laminas": { + "config-provider": "Laminas\\HttpHandlerRunner\\ConfigProvider" + } + }, + "autoload": { + "psr-4": { + "Laminas\\HttpHandlerRunner\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Execute PSR-15 RequestHandlerInterface instances and emit responses they generate.", + "homepage": "https://laminas.dev", + "keywords": [ + "components", + "laminas", + "mezzio", + "psr-15", + "psr-7" + ], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-httphandlerrunner/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-httphandlerrunner/issues", + "rss": "https://github.com/laminas/laminas-httphandlerrunner/releases.atom", + "source": "https://github.com/laminas/laminas-httphandlerrunner" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2024-10-17T20:37:17+00:00" + }, + { + "name": "league/container", + "version": "4.2.3", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/container.git", + "reference": "72f9bebe7bd623007782a40f5ec305661ab706d8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/container/zipball/72f9bebe7bd623007782a40f5ec305661ab706d8", + "reference": "72f9bebe7bd623007782a40f5ec305661ab706d8", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "psr/container": "^1.1 || ^2.0" + }, + "provide": { + "psr/container-implementation": "^1.0" + }, + "replace": { + "orno/di": "~2.0" + }, + "require-dev": { + "nette/php-generator": "^3.4", + "nikic/php-parser": "^4.10", + "phpstan/phpstan": "^0.12.47", + "phpunit/phpunit": "^8.5.17", + "roave/security-advisories": "dev-latest", + "scrutinizer/ocular": "^1.8", + "squizlabs/php_codesniffer": "^3.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev", + "dev-4.x": "4.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Container\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Phil Bennett", + "email": "mail@philbennett.co.uk", + "role": "Developer" + } + ], + "description": "A fast and intuitive dependency injection container.", + "homepage": "https://github.com/thephpleague/container", + "keywords": [ + "container", + "dependency", + "di", + "injection", + "league", + "provider", + "service" + ], + "support": { + "issues": "https://github.com/thephpleague/container/issues", + "source": "https://github.com/thephpleague/container/tree/4.2.3" + }, + "funding": [ + { + "url": "https://github.com/philipobenito", + "type": "github" + } + ], + "time": "2024-10-23T12:06:58+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/http-server-handler", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-handler.git", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side request handler", + "keywords": [ + "handler", + "http", + "http-interop", + "psr", + "psr-15", + "psr-7", + "request", + "response", + "server" + ], + "support": { + "source": "https://github.com/php-fig/http-server-handler/tree/1.0.2" + }, + "time": "2023-04-10T20:06:20+00:00" + }, + { + "name": "psr/http-server-middleware", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-middleware.git", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0", + "psr/http-server-handler": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side middleware", + "keywords": [ + "http", + "http-interop", + "middleware", + "psr", + "psr-15", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-server-middleware/issues", + "source": "https://github.com/php-fig/http-server-middleware/tree/1.0.2" + }, + "time": "2023-04-11T06:14:47+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + } + ], + "packages-dev": [ + { + "name": "myclabs/deep-copy", + "version": "1.12.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2024-06-12T14:39:25+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.3.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" + }, + "time": "2024-10-08T18:51:32+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "10.1.16", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^10.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-22T04:31:57+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T06:24:48+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T14:07:24+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:57:52+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "10.5.38", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a86773b9e887a67bc53efa9da9ad6e3f2498c132", + "reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.16", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.3", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.2", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.0", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.38" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2024-10-28T13:06:21+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:12:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:43+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:15+00:00" + }, + { + "name": "sebastian/comparator", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-18T14:56:07+00:00" + }, + { + "name": "sebastian/complexity", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68ff824baeae169ec9f2137158ee529584553799" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:37:17+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:15:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-23T08:47:14+00:00" + }, + { + "name": "sebastian/exporter", + "version": "5.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:17:12+00:00" + }, + { + "name": "sebastian/global-state", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:19:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:38:20+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:32+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:18+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:05:40+00:00" + }, + { + "name": "sebastian/type", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:10:45+00:00" + }, + { + "name": "sebastian/version", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-07T11:34:05+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=8.1" + }, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/config/Migrations/20241027060406_CreateInitialLookupTables.php b/config/Migrations/20241027060406_CreateInitialLookupTables.php new file mode 100644 index 0000000..cd60a03 --- /dev/null +++ b/config/Migrations/20241027060406_CreateInitialLookupTables.php @@ -0,0 +1,102 @@ +table('de_base_types', ['id' => false, 'primary_key' => ['de_code']]); + $table->addColumn('de_code', 'string', [ + 'default' => null, + 'limit' => 2, + 'null' => false, + ]); + $table->addColumn('name', 'string', [ + 'default' => null, + 'limit' => 45, + 'null' => false, + ]); + $table->create(); + + // entity types + $table = $this->table('de_entity_types', ['id' => false, 'primary_key' => ['entity_type_code']]); + $table->addColumn('entity_type_code', 'string', [ + 'default' => null, + 'limit' => 2, + 'null' => false, + ]); + $table->addColumn('name', 'string', [ + 'default' => null, + 'limit' => 45, + 'null' => false, + ]); + $table->create(); + + // transaction types + $table = $this->table('de_transaction_types', ['id' => false, 'primary_key' => ['transaction_type_code']]); + $table->addColumn('transaction_type_code', 'string', [ + 'default' => null, + 'limit' => 2, + 'null' => false, + ]); + $table->addColumn('name', 'string', [ + 'default' => null, + 'limit' => 45, + 'null' => false, + ]); + $table->create(); + + // bank account types + $table = $this->table('de_bank_account_types', ['id' => false, 'primary_key' => ['bank_account_type_code']]); + $table->addColumn('bank_account_type_code', 'string', [ + 'default' => null, + 'limit' => 2, + 'null' => false, + ]); + $table->addColumn('de_code', 'string', [ + 'default' => null, + 'limit' => 2, + 'null' => false, + ]); + $table->addColumn('name', 'string', [ + 'default' => null, + 'limit' => 45, + 'null' => false, + ]); +// $table->addForeignKey( +// 'de_code', +// 'de_base_types', +// 'de_code', +// [ +// 'update' => 'RESTRICT', +// 'delete' => 'RESTRICT', +// 'constraint' => 'bank_accounts_base_de_code_fk' +// ] +// ); + $table->create(); + + // internal account types + $table = $this->table('de_account_types', ['id' => false, 'primary_key' => ['account_type_code']]); + $table->addColumn('account_type_code', 'string', [ + 'default' => null, + 'limit' => 2, + 'null' => false, + ]); + $table->addColumn('name', 'string', [ + 'default' => null, + 'limit' => 45, + 'null' => false, + ]); + $table->create(); + } +} diff --git a/config/Migrations/20241028002124_CreateAccountTables.php b/config/Migrations/20241028002124_CreateAccountTables.php new file mode 100644 index 0000000..45c5089 --- /dev/null +++ b/config/Migrations/20241028002124_CreateAccountTables.php @@ -0,0 +1,118 @@ +table('de_accounts'); + $table->addColumn('account_number', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); + $table->addColumn('parent_id', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => true, + ]); + $table->addColumn('lft', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); + $table->addColumn('rght', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); + $table->addColumn('account_type_code', 'string', [ + 'default' => null, + 'limit' => 2, + 'null' => false, + ]); + $table->addColumn('name', 'string', [ + 'default' => null, + 'limit' => 255, + 'null' => false, + ]); + $table->addColumn('account_limit', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => true, + ]); + $table->addColumn('can_credit', 'boolean', [ + 'default' => true, + 'null' => false, + ]); + $table->addColumn('can_debit', 'boolean', [ + 'default' => true, + 'null' => false, + ]); + $table->addIndex('account_number', ['unique' => true]); + $table->addIndex('parent_id', ['unique' => false]); + $table->addIndex('lft', ['unique' => false]); +// $table->addForeignKey( +// 'account_type_code', +// 'de_internal_account_types', +// 'account_type_code', +// [ +// 'update' => 'RESTRICT', +// 'delete' => 'RESTRICT', +// 'constraint' => 'de_ledgers_de_account_type_code_fk', +// ] +// ); +// $table->addForeignKey( +// 'ledger_type_code', +// 'de_ledger_types', +// 'ledger_type_code', +// [ +// 'update' => 'RESTRICT', +// 'delete' => 'RESTRICT', +// 'constraint' => 'de_ledgers_de_ledger_type_code_fk', +// ] +// ); + $table->create(); + + $table = $this->table('de_account_statements', ['id' => false, 'primary_key' => ['account_statement_id']]); + $table->addColumn('account_statement_id', 'uuid', [ + 'default' => null, + 'null' => false, + ]); + $table->addColumn('account_number', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); + $table->addColumn('statement_date', 'date', [ + 'default' => null, + 'null' => false, + ]); + $table->addColumn('closing_balance', 'decimal', [ + 'default' => null, + 'precision' => 15, + 'scale' => 6, + 'null' => false, + ]); +// $table->addForeignKey( +// 'account_number', +// 'de_ledgers', +// 'account_number', +// [ +// 'update' => 'RESTRICT', +// 'delete' => 'RESTRICT', +// 'constraint' => 'ledger_statements_account_number_fk', +// ] +// ); + $table->create(); + } +} diff --git a/config/Migrations/20241028020434_CreateTransactionTables.php b/config/Migrations/20241028020434_CreateTransactionTables.php new file mode 100644 index 0000000..0394ff2 --- /dev/null +++ b/config/Migrations/20241028020434_CreateTransactionTables.php @@ -0,0 +1,277 @@ +table('de_external_accounts', ['id' => false, 'primary_key' => ['external_account_number']]); + $table->addColumn('external_account_number', 'uuid', [ + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); + $table->addColumn('entity_type_code', 'string', [ + 'default' => null, + 'limit' => 2, + 'null' => false, + ]); + $table->addColumn('created', 'datetime', [ + 'default' => null, + 'null' => false, + ]); + $table->addColumn('related_model', 'string', [ + 'default' => null, + 'limit' => 255, + 'null' => true, + ]); + $table->addColumn('related_model_fk', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => true, + ]); +// $table->addForeignKey( +// 'entity_type_code', +// 'de_entity_types', +// 'entity_type_code', +// [ +// 'update' => 'RESTRICT', +// 'delete' => 'RESTRICT', +// 'constraint' => 'de_accounts_entity_type_fk' +// ] +// ); + $table->create(); + + // de_account_statements + $table = $this->table('de_external_account_statements', ['id' => false, 'primary_key' => 'external_account_statement_id']); + $table->addColumn('external_account_statement_id', 'uuid', [ + 'default' => null, + 'null' => false, + ]); + $table->addColumn('external_account_number', 'uuid', [ + 'default' => null, + 'null' => false, + ]); + $table->addColumn('statement_date', 'date', [ + 'default' => null, + 'null' => false, + ]); + $table->addColumn('closing_balance', 'decimal', [ + 'default' => null, + 'precision' => 15, + 'scale' => 6, + 'null' => false, + ]); + $table->addColumn('total_credit', 'decimal', [ + 'default' => null, + 'precision' => 15, + 'scale' => 6, + 'null' => false, + ]); + $table->addColumn('total_debit', 'decimal', [ + 'default' => null, + 'precision' => 15, + 'scale' => 6, + 'null' => false, + ]); +// $table->addForeignKey( +// 'external_account_number', +// 'de_external_accounts', +// 'external_account_number', +// [ +// 'update' => 'RESTRICT', +// 'delete' => 'RESTRICT', +// 'constraint' => 'de_ext_acct_statements_ext_account_number_fk', +// ] +// ); + $table->create(); + + // de_journal_types + $table = $this->table('de_journal_types', ['id' => false, 'primary_key' => 'de_journal_type_code']); + $table->addColumn('de_journal_type_code', 'string', [ + 'default' => null, + 'limit' => 5, + 'null' => false, + ]); + $table->create(); + + // de_journals + $table = $this->table('de_journals'); + $table->addColumn('name', 'string', [ + 'default' => null, + 'limit' => 45, + 'null' => false, + ]); + $table->addColumn('short_code', 'string', [ + 'default' => null, + 'limit' => 5, + 'null' => false, + ]); + $table->addColumn('default_account_number_credit', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => true, + ]); + $table->addColumn('default_account_number_debit', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => true, + ]); + $table->addColumn('de_journal_type_code', 'string', [ + 'default' => null, + 'limit' => 5, + 'null' => false, + ]); +// $table->addForeignKey( +// 'default_account_number_credit', +// 'de_ledgers', +// 'account_number', +// [ +// 'update' => 'RESTRICT', +// 'delete' => 'RESTRICT', +// 'constraint' => 'de_journals_default_credit_account_number_fk', +// ] +// ); +// $table->addForeignKey( +// 'default_account_number_debit', +// 'de_ledgers', +// 'account_number', +// [ +// 'update' => 'RESTRICT', +// 'delete' => 'RESTRICT', +// 'constraint' => 'de_journals_default_debit_account_number_fk', +// ] +// ); +// $table->addForeignKey( +// 'de_journal_type_code', +// 'de_journal_types', +// 'de_journal_type_code', +// [ +// 'update' => 'RESTRICT', +// 'delete' => 'RESTRICT', +// 'constraint' => 'de_journal_types_for_journals_fk', +// ] +// ); + $table->create(); + + // de_journal_entries + $table = $this->table('de_journal_entries'); + $table->addColumn('de_journal_id', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); + $table->addColumn('user_id', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => true, + ]); + $table->addColumn('created', 'datetime', [ + 'default' => null, + 'null' => false, + ]); + $table->addColumn('notes', 'text', [ + 'default' => null, + 'null' => true, + ]); + $table->create(); + + // de_journal_items + $table = $this->table('de_journal_items'); + $table->addColumn('account_number_credit', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); + $table->addColumn('account_number_debit', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); + $table->addColumn('external_account_number_credit', 'uuid', [ + 'default' => null, + 'null' => true, + ]); + $table->addColumn('external_account_number_debit', 'uuid', [ + 'default' => null, + 'null' => true, + ]); + $table->addColumn('de_journal_entry_id', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); + + $table->addColumn('amount', 'decimal', [ + 'default' => null, + 'precision' => 15, + 'scale' => 6, + 'null' => false, + ]); + $table->addColumn('created', 'datetime', [ + 'default' => null, + 'null' => false, + ]); +// $table->addForeignKey( +// 'de_journal_entry_id', +// 'de_journal_entries', +// 'id', +// [ +// 'update' => 'RESTRICT', +// 'delete' => 'RESTRICT', +// 'constraint' => 'de_journal_items_journal_entry_id_fk', +// ] +// ); +// $table->addForeignKey( +// 'account_number_credit', +// 'de_ledgers', +// 'account_number', +// [ +// 'update' => 'RESTRICT', +// 'delete' => 'RESTRICT', +// 'constraint' => 'de_journal_items_account_number_credit_fk', +// ] +// ); +// $table->addForeignKey( +// 'account_number_debit', +// 'de_ledgers', +// 'account_number', +// [ +// 'update' => 'RESTRICT', +// 'delete' => 'RESTRICT', +// 'constraint' => 'de_journal_items_account_number_debit_fk', +// ] +// ); +// $table->addForeignKey( +// 'external_account_number_credit', +// 'de_external_accounts', +// 'external_account_number', +// [ +// 'update' => 'RESTRICT', +// 'delete' => 'RESTRICT', +// 'constraint' => 'de_journal_items_external_account_number_credit_fk', +// ] +// ); +// $table->addForeignKey( +// 'external_account_number_debit', +// 'de_external_accounts', +// 'external_account_number', +// [ +// 'update' => 'RESTRICT', +// 'delete' => 'RESTRICT', +// 'constraint' => 'de_journal_items_external_account_number_debit_fk', +// ] +// ); + $table->create(); + } +} diff --git a/config/Migrations/20241029031301_CreateExampleAccounts.php b/config/Migrations/20241029031301_CreateExampleAccounts.php new file mode 100644 index 0000000..e2280a7 --- /dev/null +++ b/config/Migrations/20241029031301_CreateExampleAccounts.php @@ -0,0 +1,102 @@ +table('de_account_templates'); + $table->addColumn('name', 'string', [ + 'default' => null, + 'limit' => 45, + 'null' => false, + ]); + $table->addColumn('deleted', 'datetime', [ + 'default' => null, + 'null' => true, + ]); + $table->create(); + + $table = $this->table('de_templated_accounts'); + $table->addColumn('account_number', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); + $table->addColumn('account_template_id', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); + $table->addColumn('parent_id', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => true, + ]); + $table->addColumn('lft', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); + $table->addColumn('rght', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); + $table->addColumn('account_type_code', 'string', [ + 'default' => null, + 'limit' => 2, + 'null' => false, + ]); + $table->addColumn('account_limit', 'integer', [ + 'default' => null, + 'limit' => 11, + 'null' => true, + ]); + $table->addColumn('can_credit', 'boolean', [ + 'default' => true, + 'null' => false, + ]); + $table->addColumn('can_debit', 'boolean', [ + 'default' => true, + 'null' => false, + ]); + $table->addColumn('name', 'string', [ + 'default' => null, + 'limit' => 255, + 'null' => false, + ]); +// $table->addForeignKey( +// 'ledger_type_code', +// 'de_ledger_types', +// 'ledger_type_code', +// [ +// 'update' => 'RESTRICT', +// 'delete' => 'RESTRICT', +// 'constraint' => 'de_templated_ledgers_de_ledger_type_fk', +// ] +// ); +// $table->addForeignKey( +// 'account_type_code', +// 'de_internal_account_types', +// 'account_type_code', +// [ +// 'update' => 'RESTRICT', +// 'delete' => 'RESTRICT', +// 'constraint' => 'de_templated_ledgers_de_account_type_code_fk', +// ] +// ); + $table->addIndex('parent_id', ['unique' => false]); + $table->addIndex('lft', ['unique' => false]); + $table->create(); + } +} diff --git a/config/Migrations/schema-dump-default.lock b/config/Migrations/schema-dump-default.lock new file mode 100644 index 0000000..7fab4de Binary files /dev/null and b/config/Migrations/schema-dump-default.lock differ diff --git a/config/Seeds/BareBonesAccountTemplateSeed.php b/config/Seeds/BareBonesAccountTemplateSeed.php new file mode 100644 index 0000000..cfaa98a --- /dev/null +++ b/config/Seeds/BareBonesAccountTemplateSeed.php @@ -0,0 +1,283 @@ +query('SELECT * FROM de_account_templates'); // returns PDOStatement + $existing = $stmt->fetchAll(); // returns the result as an array + if (!$existing) { + $data = [ + [ + 'id' => 1, + 'name' => 'Basic', + ] + ]; + + $table = $this->table('de_account_templates'); + $table->insert($data)->save(); + + $templatedAccountsTable = TableRegistry::getTableLocator()->get('CakeAccounting.DeTemplatedAccounts'); + + $roots = [ + [ + 'account_number' => 10000, + 'account_template_id' => 1, + 'parent_id' => null, + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Assets', + ], + [ + 'account_number' => 20000, + 'account_template_id' => 1, + 'parent_id' => null, + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, + 'name' => 'Liabilities', + ], + [ + 'account_number' => 30000, + 'account_template_id' => 1, + 'parent_id' => null, + 'account_type_code' => DE_ACCOUNT_TYPE_EQUITY, + 'name' => 'Equity', + ], + [ + 'account_number' => 40000, + 'account_template_id' => 1, + 'parent_id' => null, + 'account_type_code' => DE_ACCOUNT_TYPE_REVENUE, + 'name' => 'Revenue', + ], + [ + 'account_number' => 50000, + 'account_template_id' => 1, + 'parent_id' => null, + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Expenses', + ], + ]; + $added = []; + foreach ($roots as $root) { + $rootEntity = $templatedAccountsTable->newEntity($root); + $saveResultRootEntity = $templatedAccountsTable->save($rootEntity); + if (!$saveResultRootEntity) { + Log::debug(print_r('$rootEntity->getErrors()', true)); + Log::debug(print_r($rootEntity->getErrors(), true)); + + continue; + } + $added[$saveResultRootEntity->account_number] = $saveResultRootEntity->id; + } + $children = [ + [ + 'account_number' => 11000, + 'account_template_id' => 1, + 'parent_id' => 10000, + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Cash', + ], + [ + 'account_number' => 12000, + 'account_template_id' => 1, + 'parent_id' => 10000, + + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Investments', + ], + [ + 'account_number' => 13000, + 'account_template_id' => 1, + 'parent_id' => 10000, + + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Accounts Receivable', + ], + [ + 'account_number' => 14000, + 'account_template_id' => 1, + 'parent_id' => 10000, + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Inventory', + ], + [ + 'account_number' => 15000, + 'account_template_id' => 1, + 'parent_id' => 10000, + + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Prepaid Expenses', + ], + [ + 'account_number' => 16000, + 'account_template_id' => 1, + 'parent_id' => 10000, + + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Property & Equipment', + ], + // liabilities + [ + 'account_number' => 21000, + 'account_template_id' => 1, + 'parent_id' => 20000, + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, + 'name' => 'Accounts Payable', + ], + [ + 'account_number' => 22000, + 'account_template_id' => 1, + 'parent_id' => 20000, + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, + 'name' => 'Accrued Payroll Expenses', + ], + [ + 'account_number' => 23000, + 'account_template_id' => 1, + 'parent_id' => 20000, + + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, + 'name' => 'Accrued Expenses', + ], + [ + 'account_number' => 24000, + 'account_template_id' => 1, + 'parent_id' => 20000, + + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, + 'name' => 'Taxes', + ], + [ + 'account_number' => 25000, + 'account_template_id' => 1, + 'parent_id' => 20000, + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, + 'name' => 'Long Term Debt', + ], + // equity + [ + 'account_number' => 31000, + 'account_template_id' => 1, + 'parent_id' => 30000, + + 'account_type_code' => DE_ACCOUNT_TYPE_EQUITY, + 'name' => 'Owner\'s Equity', + ], + [ + 'account_number' => 32000, + 'account_template_id' => 1, + 'parent_id' => 30000, + 'account_type_code' => DE_ACCOUNT_TYPE_EQUITY, + 'name' => 'Retained Earnings', + ], + // revenue + [ + 'account_number' => 41000, + 'account_template_id' => 1, + 'parent_id' => 40000, + + 'account_type_code' => DE_ACCOUNT_TYPE_REVENUE, + 'name' => 'Sales', + ], + [ + 'account_number' => 41100, + 'account_template_id' => 1, + 'parent_id' => 41000, + 'account_type_code' => DE_ACCOUNT_TYPE_REVENUE, + 'name' => 'Product Sales', + ], + [ + 'account_number' => 41200, + 'account_template_id' => 1, + 'parent_id' => 41000, + 'account_type_code' => DE_ACCOUNT_TYPE_REVENUE, + 'name' => 'Service Sales', + ], + // expenses + [ + 'account_number' => 51000, + 'account_template_id' => 1, + 'parent_id' => 50000, + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Payroll Expenses', + ], + [ + 'account_number' => 52000, + 'account_template_id' => 1, + 'parent_id' => 50000, + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Rent & Utilities', + ], + [ + 'account_number' => 53000, + 'account_template_id' => 1, + 'parent_id' => 50000, + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Marketing', + ], + [ + 'account_number' => 54000, + 'account_template_id' => 1, + 'parent_id' => 50000, + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Insurance', + ], + [ + 'account_number' => 55000, + 'account_template_id' => 1, + 'parent_id' => 50000, + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Cost of Goods Sold', + ], + [ + 'account_number' => 56000, + 'account_template_id' => 1, + 'parent_id' => 50000, + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Other Expenses', + ], + ]; + foreach ($children as $child) { + $child['parent_id'] = $added[$child['parent_id']]; + $childEntity = $templatedAccountsTable->newEntity($child); + $saveResultChildEntity = $templatedAccountsTable->save($childEntity); + if (!$saveResultChildEntity) { + Log::debug(print_r('$childEntity->getErrors()', true)); + Log::debug(print_r($childEntity->getErrors(), true)); + + continue; + } + $added[$saveResultChildEntity->account_number] = $saveResultChildEntity->id; + } + } + + } +} diff --git a/config/Seeds/JournalsTablesSeed.php b/config/Seeds/JournalsTablesSeed.php new file mode 100644 index 0000000..56b507b --- /dev/null +++ b/config/Seeds/JournalsTablesSeed.php @@ -0,0 +1,86 @@ +query('SELECT * FROM de_journal_types'); // returns PDOStatement + $existing = $stmt->fetchAll(); // returns the result as an array + if (!$existing) { + $journalTypes = [ + [ + 'de_journal_type_code' => DE_JOURNAL_TYPE_SALES, + ], + [ + 'de_journal_type_code' => DE_JOURNAL_TYPE_PURCHASING, + ], + [ + 'de_journal_type_code' => DE_JOURNAL_TYPE_BANK, + ], + [ + 'de_journal_type_code' => DE_JOURNAL_TYPE_MISC, + ], + [ + 'de_journal_type_code' => DE_JOURNAL_TYPE_CASH, + ], + ]; + + $table = $this->table('de_journal_types'); + $table->insert($journalTypes)->save(); + } + + + // entity types + $stmt = $this->query('SELECT * FROM de_journals'); // returns PDOStatement + $existing = $stmt->fetchAll(); // returns the result as an array + if (!$existing) { + $entityTypes = [ + [ + 'name' => 'Customer Invoices', + 'de_journal_type_code' => DE_JOURNAL_TYPE_SALES, + 'short_code' => 'SALES', + ], + [ + 'name' => 'Vendor Bills', + 'de_journal_type_code' => DE_JOURNAL_TYPE_PURCHASING, + 'short_code' => 'BILLS', + ], + [ + 'name' => 'Bank', + 'de_journal_type_code' => DE_JOURNAL_TYPE_BANK, + 'short_code' => 'BANK', + ], + [ + 'name' => 'Misc.', + 'de_journal_type_code' => DE_JOURNAL_TYPE_MISC, + 'short_code' => 'MISC', + ], + [ + 'name' => 'Cash', + 'de_journal_type_code' => DE_JOURNAL_TYPE_CASH, + 'short_code' => 'CASH', + ], + ]; + + $table = $this->table('de_journals'); + $table->insert($entityTypes)->save(); + } + } +} diff --git a/config/Seeds/SeedLookupTablesSeed.php b/config/Seeds/SeedLookupTablesSeed.php new file mode 100644 index 0000000..bbd2963 --- /dev/null +++ b/config/Seeds/SeedLookupTablesSeed.php @@ -0,0 +1,175 @@ +query('SELECT * FROM de_base_types'); // returns PDOStatement + $existing = $stmt->fetchAll(); // returns the result as an array + if (!$existing) { + $baseTypes = [ + [ + 'de_code' => DE_TYPE_CREDIT, + 'name' => 'Credit', + ], + [ + 'de_code' => DE_TYPE_DEBIT, + 'name' => 'Debit', + ], + ]; + + $table = $this->table('de_base_types'); + $table->insert($baseTypes)->save(); + } + + + // entity types + $stmt = $this->query('SELECT * FROM de_entity_types'); // returns PDOStatement + $existing = $stmt->fetchAll(); // returns the result as an array + if (!$existing) { + $entityTypes = [ + [ + 'entity_type_code' => DE_ENTITY_TYPE_PERSON, + 'name' => 'Person', + ], + [ + 'entity_type_code' => DE_ENTITY_TYPE_ORGANIZATION, + 'name' => 'Organization', + ], + [ + 'entity_type_code' => DE_ENTITY_TYPE_PROPERTY, + 'name' => 'Property', + ], + [ + 'entity_type_code' => DE_ENTITY_TYPE_BANK_ACCOUNT, + 'name' => 'Bank Account', + ], + ]; + + $table = $this->table('de_entity_types'); + $table->insert($entityTypes)->save(); + } + + // transaction types + $stmt = $this->query('SELECT * FROM de_transaction_types'); // returns PDOStatement + $existing = $stmt->fetchAll(); // returns the result as an array + if (!$existing) { + $transactionTypes = [ + [ + 'transaction_type_code' => DE_TRANSACTION_TYPE_ADJUST_CREDIT, + 'name' => 'Adjust Credit', + ], + [ + 'transaction_type_code' => DE_TRANSACTION_TYPE_ADJUST_DEBIT, + 'name' => 'Adjust Debit', + ], + [ + 'transaction_type_code' => DE_TRANSACTION_TYPE_DEPOSIT, + 'name' => 'Deposit', + ], + [ + 'transaction_type_code' => DE_TRANSACTION_TYPE_WITHDRAW, + 'name' => 'Withdrawal', + ], + [ + 'transaction_type_code' => DE_TRANSACTION_TYPE_ESTIMATE_CREDIT, + 'name' => 'Estimate Credit', + ], + [ + 'transaction_type_code' => DE_TRANSACTION_TYPE_ESTIMATE_DEBIT, + 'name' => 'Estimate Debit', + ] + ]; + + $table = $this->table('de_transaction_types'); + $table->insert($transactionTypes)->save(); + } + + // bank account types + $stmt = $this->query('SELECT * FROM de_bank_account_types'); // returns PDOStatement + $existing = $stmt->fetchAll(); // returns the result as an array + if (!$existing) { + $bankAccountTypes = [ + [ + 'bank_account_type_code' => DE_BANK_ACCOUNT_TYPE_CHECKING, + 'de_code' => DE_TYPE_CREDIT, + 'name' => 'Checking', + ], + [ + 'bank_account_type_code' => DE_BANK_ACCOUNT_TYPE_SAVINGS, + 'de_code' => DE_TYPE_CREDIT, + 'name' => 'Savings', + ], + [ + 'bank_account_type_code' => DE_BANK_ACCOUNT_TYPE_LINE_OF_CREDIT, + 'de_code' => DE_TYPE_DEBIT, + 'name' => 'Line of Credit', + ], + [ + 'bank_account_type_code' => DE_BANK_ACCOUNT_TYPE_MORTGAGE, + 'de_code' => DE_TYPE_DEBIT, + 'name' => 'Mortgage', + ], + ]; + + $table = $this->table('de_bank_account_types'); + $table->insert($bankAccountTypes)->save(); + } + + // account types + $stmt = $this->query('SELECT * FROM de_account_types'); // returns PDOStatement + $existing = $stmt->fetchAll(); // returns the result as an array + if (!$existing) { + $internalAccountTypes = [ + [ + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Asset', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, + 'name' => 'Liability', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_REVENUE, + 'name' => 'Revenue', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Expenses', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_EQUITY, + 'name' => 'Equity', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_GAIN, + 'name' => 'Gain', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_LOSS, + 'name' => 'Loss', + ], + ]; + + $table = $this->table('de_account_types'); + $table->insert($internalAccountTypes)->save(); + } + } +} diff --git a/config/bootstrap.php b/config/bootstrap.php new file mode 100644 index 0000000..ac8572b --- /dev/null +++ b/config/bootstrap.php @@ -0,0 +1,7 @@ + + + + + + + + + + + tests/TestCase/ + + + + + + + + + + + src/ + + + diff --git a/src/CakeAccountingPlugin.php b/src/CakeAccountingPlugin.php new file mode 100644 index 0000000..f1d93f1 --- /dev/null +++ b/src/CakeAccountingPlugin.php @@ -0,0 +1,99 @@ +plugin( + 'CakeAccounting', + ['path' => '/cake-accounting'], + function (RouteBuilder $builder) { + // Add custom routes here + + $builder->fallbacks(); + } + ); + parent::routes($routes); + } + + /** + * Add middleware for the plugin. + * + * @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to update. + * @return \Cake\Http\MiddlewareQueue + */ + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + // Add your middlewares here + + return $middlewareQueue; + } + + /** + * Add commands for the plugin. + * + * @param \Cake\Console\CommandCollection $commands The command collection to update. + * @return \Cake\Console\CommandCollection + */ + public function console(CommandCollection $commands): CommandCollection + { + // Add your commands here + + $commands = parent::console($commands); + + return $commands; + } + + /** + * Register application container services. + * + * @param \Cake\Core\ContainerInterface $container The Container to update. + * @return void + * @link https://book.cakephp.org/4/en/development/dependency-injection.html#dependency-injection + */ + public function services(ContainerInterface $container): void + { + // Add your services here + $container->add(AccountingService::class); + } +} diff --git a/src/Controller/AppController.php b/src/Controller/AppController.php new file mode 100644 index 0000000..598a176 --- /dev/null +++ b/src/Controller/AppController.php @@ -0,0 +1,10 @@ + + */ + protected array $_defaultConfig = []; +} diff --git a/src/Controller/DeAccountStatementsController.php b/src/Controller/DeAccountStatementsController.php new file mode 100644 index 0000000..f0c1194 --- /dev/null +++ b/src/Controller/DeAccountStatementsController.php @@ -0,0 +1,105 @@ +DeAccountStatements->find() + ->contain(['DeAccounts']); + $deAccountStatements = $this->paginate($query); + + $this->set(compact('deAccountStatements')); + } + + /** + * View method + * + * @param string|null $id De Account Statement id. + * @return \Cake\Http\Response|null|void Renders view + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function view($id = null) + { + $deAccountStatement = $this->DeAccountStatements->get($id, contain: ['DeAccounts']); + $this->set(compact('deAccountStatement')); + } + + /** + * Add method + * + * @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise. + */ + public function add() + { + $deAccountStatement = $this->DeAccountStatements->newEmptyEntity(); + if ($this->request->is('post')) { + $deAccountStatement = $this->DeAccountStatements->patchEntity($deAccountStatement, $this->request->getData()); + if ($this->DeAccountStatements->save($deAccountStatement)) { + $this->Flash->success(__('The de account statement has been saved.')); + + return $this->redirect(['action' => 'index']); + } + $this->Flash->error(__('The de account statement could not be saved. Please, try again.')); + } + $deAccounts = $this->DeAccountStatements->DeAccounts->find('list', limit: 200)->all(); + $this->set(compact('deAccountStatement', 'deAccounts')); + } + + /** + * Edit method + * + * @param string|null $id De Account Statement id. + * @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise. + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function edit($id = null) + { + $deAccountStatement = $this->DeAccountStatements->get($id, contain: []); + if ($this->request->is(['patch', 'post', 'put'])) { + $deAccountStatement = $this->DeAccountStatements->patchEntity($deAccountStatement, $this->request->getData()); + if ($this->DeAccountStatements->save($deAccountStatement)) { + $this->Flash->success(__('The de account statement has been saved.')); + + return $this->redirect(['action' => 'index']); + } + $this->Flash->error(__('The de account statement could not be saved. Please, try again.')); + } + $deAccounts = $this->DeAccountStatements->DeAccounts->find('list', limit: 200)->all(); + $this->set(compact('deAccountStatement', 'deAccounts')); + } + + /** + * Delete method + * + * @param string|null $id De Account Statement id. + * @return \Cake\Http\Response|null Redirects to index. + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function delete($id = null) + { + $this->request->allowMethod(['post', 'delete']); + $deAccountStatement = $this->DeAccountStatements->get($id); + if ($this->DeAccountStatements->delete($deAccountStatement)) { + $this->Flash->success(__('The de account statement has been deleted.')); + } else { + $this->Flash->error(__('The de account statement could not be deleted. Please, try again.')); + } + + return $this->redirect(['action' => 'index']); + } +} diff --git a/src/Controller/DeAccountTemplatesController.php b/src/Controller/DeAccountTemplatesController.php new file mode 100644 index 0000000..7e71a13 --- /dev/null +++ b/src/Controller/DeAccountTemplatesController.php @@ -0,0 +1,142 @@ +DeAccountTemplates->find(); + $deAccountTemplates = $this->paginate($query); + + $this->set(compact('deAccountTemplates')); + } + + /** + * select method + * + * @param string|null $id De Account Template id. + * @return \Cake\Http\Response|null Redirects to index. + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function select($id = null) + { + $mainDeAccountsTable = $this->getTableLocator()->get('CakeAccounting.DeAccounts'); + + $this->request->allowMethod(['post']); + $deAccountTemplate = $this->DeAccountTemplates->get($id); + + $accounts = $this->DeAccountTemplates->DeTemplatedAccounts->find('threaded') + ->where(['account_template_id' => $id]) +// ->where(['parent_id IS' => null]) + ->toArray(); + + $newAccountData = []; + $added = []; + foreach ($accounts as $account) { + $mainDeAccountsTable->importAccountFromTemplatedAccount($account); + } + + $newAccounts = $mainDeAccountsTable->find()->count(); + if ($newAccounts >= count($accounts)) { + $this->Flash->success(__('Imported Accounts from Accounts Template: ' . $deAccountTemplate->name)); + } else { + $this->Flash->error(__('Failed to import Accounts from Accounts Template: ' . $deAccountTemplate->name)); + } + + return $this->redirect([ + 'controller' => 'DeAccounts', + 'action' => 'index' + ]); + } + + /** + * View method + * + * @param string|null $id De Account Template id. + * @return \Cake\Http\Response|null|void Renders view + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function view($id = null) + { + $deAccountTemplate = $this->DeAccountTemplates->get($id, contain: ['DeTemplatedAccounts']); + $this->set(compact('deAccountTemplate')); + } + + /** + * Add method + * + * @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise. + */ + public function add() + { + $deAccountTemplate = $this->DeAccountTemplates->newEmptyEntity(); + if ($this->request->is('post')) { + $postData = $this->request->getData(); + $deAccountTemplate = $this->DeAccountTemplates->patchEntity($deAccountTemplate, $postData); + if ($this->DeAccountTemplates->save($deAccountTemplate)) { + $this->Flash->success(__('The de account template has been saved.')); + + return $this->redirect(['action' => 'index']); + } + $this->Flash->error(__('The de account template could not be saved. Please, try again.')); + } + $this->set(compact('deAccountTemplate')); + } + + /** + * Edit method + * + * @param string|null $id De Account Template id. + * @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise. + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function edit($id = null) + { + $deAccountTemplate = $this->DeAccountTemplates->get($id, contain: []); + if ($this->request->is(['patch', 'post', 'put'])) { + $deAccountTemplate = $this->DeAccountTemplates->patchEntity($deAccountTemplate, $this->request->getData()); + if ($this->DeAccountTemplates->save($deAccountTemplate)) { + $this->Flash->success(__('The de account template has been saved.')); + + return $this->redirect(['action' => 'index']); + } + $this->Flash->error(__('The de account template could not be saved. Please, try again.')); + } + $this->set(compact('deAccountTemplate')); + } + + /** + * Delete method + * + * @param string|null $id De Account Template id. + * @return \Cake\Http\Response|null Redirects to index. + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function delete($id = null) + { + $this->request->allowMethod(['post', 'delete']); + $deAccountTemplate = $this->DeAccountTemplates->get($id); + if ($this->DeAccountTemplates->delete($deAccountTemplate)) { + $this->Flash->success(__('The de account template has been deleted.')); + } else { + $this->Flash->error(__('The de account template could not be deleted. Please, try again.')); + } + + return $this->redirect(['action' => 'index']); + } +} diff --git a/src/Controller/DeAccountsController.php b/src/Controller/DeAccountsController.php new file mode 100644 index 0000000..1533aa7 --- /dev/null +++ b/src/Controller/DeAccountsController.php @@ -0,0 +1,177 @@ +viewBuilder()->addHelper('CakeAccounting.Accounting'); + } + + /** + * Index method + * + * @return \Cake\Http\Response|null|void Renders view + */ + public function index(AccountingService $accounting) + { + $deAccounts = $this->DeAccounts->find('threaded')->contain(['DeAccountTypes'])->toArray(); + $totals = []; + foreach ($deAccounts as $deAccount) { +// Log::debug('account in de accounts controller index for loop'); +// Log::debug('$deAccount->account_number'); +// Log::debug("$deAccount->account_number"); + + $totals[$deAccount->account_number] = $accounting->getAccountBalance($deAccount); + } + + $this->set(compact('deAccounts', 'totals')); + } + + /** + * View method + * + * @param string|null $accountNumber De Account account number. + * @return \Cake\Http\Response|null|void Renders view + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function view(AccountingService $accounting, $accountNumber = null, ) + { + $deAccount = $this->DeAccounts->find() + ->contain(['DeAccountTypes', 'DeAccountStatements']) + ->where(['DeAccounts.account_number' => $accountNumber]) + ->firstOrFail(); + $deAccount->children = $this->DeAccounts->find('children', for: $deAccount->id)->find('threaded')->toArray(); + $totals[$deAccount->account_number] = $accounting->getAccountBalance($deAccount); + + $this->set(compact('deAccount', 'totals')); + } + + /** + * Add method + * + * @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise. + */ + public function add() + { + $deAccount = $this->DeAccounts->newEmptyEntity(); + if ($this->request->is('post')) { + $postData = $this->request->getData(); + if ($this->request->getData('parent_id')) { + $parent = $this->DeAccounts->find()->where(['DeAccounts.account_number' => $this->request->getData('parent_id')])->first(); + if (isset($parent)) { + $postData['parent_id'] = $parent->id; + $postData['account_type_code'] = $parent->account_type_code; + } + } + $deAccount = $this->DeAccounts->patchEntity($deAccount, $postData); + if ($this->DeAccounts->save($deAccount)) { + $this->Flash->success(__('The de account has been saved.')); + + return $this->redirect(['action' => 'index']); + } + Log::debug(print_r('$deAccount->getErrors() adding', true)); + Log::debug(print_r($deAccount->getErrors(), true)); + $this->Flash->error(__('The de account could not be saved. Please, try again.')); + } + $deAccountTypes = $this->DeAccounts->DeAccountTypes->find('list', limit: 200)->all(); + $accounts = $this->DeAccounts->find('accountTreeList')->toArray(); + $this->set(compact('deAccount', 'deAccountTypes', 'accounts')); + } + + /** + * Edit method + * + * @param string|null $accountNumber De Account account number. + * @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise. + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function edit($accountNumber = null) + { + $deAccount = $this->DeAccounts->find() + ->where(['DeAccounts.account_number' => $accountNumber]) + ->firstOrFail(); + $children = $this->DeAccounts->find()->where(['parent_id' => $deAccount->id])->count(); + $journalItems = $this->DeAccounts->CreditDeJournalItems->find() + ->where([ + 'OR' => [ + 'account_number_credit' => $accountNumber, + 'account_number_debit' => $accountNumber, + ], + ]) + ->count(); + if ($children) { + $this->Flash->error('Account cannot be edited as there are are sub-accounts underneath this account.'); + } + if ($journalItems) { + $this->Flash->error('Account cannot be edited as journal entries already exist on this account.'); + } + if ($children || $journalItems) { + return $this->redirect(['action' => 'view', $accountNumber]); + } + if ($this->request->is(['patch', 'post', 'put'])) { + $postData = $this->request->getData(); + if ($this->request->getData('parent_id')) { + $parent = $this->DeAccounts->find()->where(['DeAccounts.account_number' => $this->request->getData('parent_id')])->first(); + if (isset($parent)) { + $postData['parent_id'] = $parent->id; + $postData['account_type_code'] = $parent->account_type_code; + } + } + + $deAccount = $this->DeAccounts->patchEntity($deAccount, $postData); + if ($this->DeAccounts->save($deAccount)) { + $this->Flash->success(__('The de account has been saved.')); + + return $this->redirect(['action' => 'index']); + } + Log::debug(print_r('$deAccount->getErrors() editing', true)); + Log::debug(print_r($deAccount->getErrors(), true)); + + $this->Flash->error(__('The de account could not be saved. Please, try again.')); + } + $deAccountTypes = $this->DeAccounts->DeAccountTypes->find('list', limit: 200)->all(); + $parentDeAccounts = $this->DeAccounts->ParentDeAccounts->find('list', limit: 200)->all(); + $this->set(compact('deAccount', 'deAccountTypes', 'parentDeAccounts')); + } + + /** + * Delete method + * + * @param string|null $accountNumber De Account id. + * @return \Cake\Http\Response|null Redirects to index. + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function delete($accountNumber = null) + { + $this->request->allowMethod(['post', 'delete']); + $deAccount = $this->DeAccounts->find() + ->where(['DeAccounts.account_number' => $accountNumber]) + ->firstOrFail(); + if ($this->DeAccounts->delete($deAccount)) { + $this->Flash->success(__('The de account has been deleted.')); + } else { + $this->Flash->error(__('The de account could not be deleted. Please, try again.')); + } + + return $this->redirect(['action' => 'index']); + } +} diff --git a/src/Controller/DeExternalAccountStatementsController.php b/src/Controller/DeExternalAccountStatementsController.php new file mode 100644 index 0000000..77dad53 --- /dev/null +++ b/src/Controller/DeExternalAccountStatementsController.php @@ -0,0 +1,43 @@ +DeExternalAccountStatements->find(); + $deExternalAccountStatements = $this->paginate($query); + + $this->set(compact('deExternalAccountStatements')); + } + + /** + * View method + * + * @param string|null $id De External Account Statement id. + * @return Response|null|void Renders view + * @throws RecordNotFoundException When record not found. + */ + public function view($id = null) + { + $deExternalAccountStatement = $this->DeExternalAccountStatements->get($id, contain: []); + $this->set(compact('deExternalAccountStatement')); + } +} diff --git a/src/Controller/DeExternalAccountsController.php b/src/Controller/DeExternalAccountsController.php new file mode 100644 index 0000000..aa6d113 --- /dev/null +++ b/src/Controller/DeExternalAccountsController.php @@ -0,0 +1,102 @@ +DeExternalAccounts->find(); + $deExternalAccounts = $this->paginate($query); + + $this->set(compact('deExternalAccounts')); + } + + /** + * View method + * + * @param string|null $id De External Account id. + * @return \Cake\Http\Response|null|void Renders view + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function view($id = null) + { + $deExternalAccount = $this->DeExternalAccounts->get($id, contain: []); + $this->set(compact('deExternalAccount')); + } + + /** + * Add method + * + * @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise. + */ + public function add() + { + $deExternalAccount = $this->DeExternalAccounts->newEmptyEntity(); + if ($this->request->is('post')) { + $deExternalAccount = $this->DeExternalAccounts->patchEntity($deExternalAccount, $this->request->getData()); + if ($this->DeExternalAccounts->save($deExternalAccount)) { + $this->Flash->success(__('The de external account has been saved.')); + + return $this->redirect(['action' => 'index']); + } + $this->Flash->error(__('The de external account could not be saved. Please, try again.')); + } + $this->set(compact('deExternalAccount')); + } + + /** + * Edit method + * + * @param string|null $id De External Account id. + * @return \Cake\Http\Response|null|void Redirects on successful edit, renders view otherwise. + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function edit($id = null) + { + $deExternalAccount = $this->DeExternalAccounts->get($id, contain: []); + if ($this->request->is(['patch', 'post', 'put'])) { + $deExternalAccount = $this->DeExternalAccounts->patchEntity($deExternalAccount, $this->request->getData()); + if ($this->DeExternalAccounts->save($deExternalAccount)) { + $this->Flash->success(__('The de external account has been saved.')); + + return $this->redirect(['action' => 'index']); + } + $this->Flash->error(__('The de external account could not be saved. Please, try again.')); + } + $this->set(compact('deExternalAccount')); + } + + /** + * Delete method + * + * @param string|null $id De External Account id. + * @return \Cake\Http\Response|null Redirects to index. + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function delete($id = null) + { + $this->request->allowMethod(['post', 'delete']); + $deExternalAccount = $this->DeExternalAccounts->get($id); + if ($this->DeExternalAccounts->delete($deExternalAccount)) { + $this->Flash->success(__('The de external account has been deleted.')); + } else { + $this->Flash->error(__('The de external account could not be deleted. Please, try again.')); + } + + return $this->redirect(['action' => 'index']); + } +} diff --git a/src/Controller/DeJournalEntriesController.php b/src/Controller/DeJournalEntriesController.php new file mode 100644 index 0000000..229d284 --- /dev/null +++ b/src/Controller/DeJournalEntriesController.php @@ -0,0 +1,79 @@ +DeJournalEntries->find() + ->contain(['DeJournals']); + $deJournalEntries = $this->paginate($query); + + $this->set(compact('deJournalEntries')); + } + + /** + * View method + * + * @param string|null $id De Journal Entry id. + * @return \Cake\Http\Response|null|void Renders view + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function view($id = null) + { + $deJournalEntry = $this->DeJournalEntries->get($id, + contain: ['DeJournals', 'DeJournalItems'] + ); + $this->set(compact('deJournalEntry')); + } + + /** + * Add method + * + * @return \Cake\Http\Response|null|void Redirects on successful add, renders view otherwise. + */ + public function add() + { + $deJournalEntry = $this->DeJournalEntries->newEmptyEntity(); + if ($this->request->is('post')) { + $postData = $this->request->getData(); + $saveOptions = [ + 'associated' => [ + 'DeJournalItems', + ], + ]; + if ($this->request->getSession()->read('Auth.User.id')) { + $postData['user_id'] = $this->request->getSession()->read('Auth.User.id'); + } + $deJournalEntry = $this->DeJournalEntries->patchEntity($deJournalEntry, $postData, $saveOptions); + if ($this->DeJournalEntries->save($deJournalEntry, $saveOptions)) { + $this->Flash->success(__('The de journal entry has been saved.')); + + return $this->redirect(['action' => 'index']); + } + Log::debug(print_r('$deJournalEntry->getErrors()', true)); + Log::debug(print_r($deJournalEntry->getErrors(), true)); + $this->Flash->error(__('The de journal entry could not be saved. Please, try again.')); + } + $deJournals = $this->DeJournalEntries->DeJournals->find('list', limit: 200)->all(); + $accounts = $this->DeJournalEntries->DeJournalItems->CreditDeAccounts->find('accountTreeList')->toArray(); + + $this->set(compact('deJournalEntry', 'deJournals', 'accounts')); + } +} diff --git a/src/Controller/DeJournalItemsController.php b/src/Controller/DeJournalItemsController.php new file mode 100644 index 0000000..230dd02 --- /dev/null +++ b/src/Controller/DeJournalItemsController.php @@ -0,0 +1,45 @@ +DeJournalItems->find()->contain([ + 'DeJournalEntries', + 'DeJournalEntries.DeJournals', + 'DebitDeAccounts', + 'CreditDeAccounts', + ]); + $deJournalItems = $this->paginate($query); + + $this->set(compact('deJournalItems')); + } + + /** + * View method + * + * @param string|null $id De Journal Item id. + * @return \Cake\Http\Response|null|void Renders view + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function view($id = null) + { + $deJournalItem = $this->DeJournalItems->get($id, contain: ['DeJournalEntries']); + $this->set(compact('deJournalItem')); + } +} diff --git a/src/Controller/DeJournalsController.php b/src/Controller/DeJournalsController.php new file mode 100644 index 0000000..34fc7ee --- /dev/null +++ b/src/Controller/DeJournalsController.php @@ -0,0 +1,105 @@ +DeJournals->find(); + $deJournals = $this->paginate($query); + + $this->set(compact('deJournals')); + } + + /** + * View method + * + * @param string|null $id De Journal id. + * @return Response|null|void Renders view + * @throws RecordNotFoundException When record not found. + */ + public function view($id = null) + { + $deJournal = $this->DeJournals->get($id, contain: ['DeJournalEntries']); + $this->set(compact('deJournal')); + } + + /** + * Add method + * + * @return Response|null|void Redirects on successful add, renders view otherwise. + */ + public function add() + { + $deJournal = $this->DeJournals->newEmptyEntity(); + if ($this->request->is('post')) { + $deJournal = $this->DeJournals->patchEntity($deJournal, $this->request->getData()); + if ($this->DeJournals->save($deJournal)) { + $this->Flash->success(__('The de journal has been saved.')); + + return $this->redirect(['action' => 'index']); + } + $this->Flash->error(__('The de journal could not be saved. Please, try again.')); + } + $this->set(compact('deJournal')); + } + + /** + * Edit method + * + * @param string|null $id De Journal id. + * @return Response|null|void Redirects on successful edit, renders view otherwise. + * @throws RecordNotFoundException When record not found. + */ + public function edit($id = null) + { + $deJournal = $this->DeJournals->get($id, contain: []); + if ($this->request->is(['patch', 'post', 'put'])) { + $deJournal = $this->DeJournals->patchEntity($deJournal, $this->request->getData()); + if ($this->DeJournals->save($deJournal)) { + $this->Flash->success(__('The de journal has been saved.')); + + return $this->redirect(['action' => 'index']); + } + $this->Flash->error(__('The de journal could not be saved. Please, try again.')); + } + $this->set(compact('deJournal')); + } + + /** + * Delete method + * + * @param string|null $id De Journal id. + * @return Response|null Redirects to index. + * @throws RecordNotFoundException When record not found. + */ + public function delete($id = null) + { + $this->request->allowMethod(['post', 'delete']); + $deJournal = $this->DeJournals->get($id); + if ($this->DeJournals->delete($deJournal)) { + $this->Flash->success(__('The de journal has been deleted.')); + } else { + $this->Flash->error(__('The de journal could not be deleted. Please, try again.')); + } + + return $this->redirect(['action' => 'index']); + } +} diff --git a/src/Controller/DeLedgerTemplatesController.php b/src/Controller/DeLedgerTemplatesController.php new file mode 100644 index 0000000..272949a --- /dev/null +++ b/src/Controller/DeLedgerTemplatesController.php @@ -0,0 +1,100 @@ +DeAccountTemplates->find(); + $deAccountTemplates = $this->paginate($query); + + $this->set(compact('deAccountTemplates')); + } + + /** + * View method + * + * @param string|null $id De Account Template id. + * @return \Cake\Http\Response|null|void Renders view + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function view($id = null) + { + $deAccountTemplate = $this->DeAccountTemplates->get($id, contain: []); + $this->set(compact('deAccountTemplate')); + } + + /** + * select method + * + * @param string|null $id De Account Template id. + * @return \Cake\Http\Response|null Redirects to index. + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function select($id = null) + { + $mainDeAccountsTable = $this->getTableLocator()->get('CakeAccounting.DeAccounts'); + + $this->request->allowMethod(['post']); + $deAccountTemplate = $this->DeAccountTemplates->get($id); + + $ledgers = $this->DeAccountTemplates->DeTemplatedAccounts->find('threaded') + ->where(['account_template_id' => $id]) +// ->where(['parent_id IS' => null]) + ->toArray(); +// Log::debug(print_r('$rootAccounts', true)); +// Log::debug(print_r($rootAccounts, true)); + + $newAccountData = []; + $added = []; + foreach ($ledgers as $ledger) { + $importResult = $mainDeAccountsTable->importAccountFromTemplatedAccount($ledger); + Log::debug(print_r('$importResult', true)); + Log::debug(print_r($importResult, true)); + } + + $newAccounts = $mainDeAccountsTable->find()->count(); + if ($newAccounts >= count($ledgers)) { + $this->Flash->success(__('Imported Accounts from Accounts Template: ' . $deAccountTemplate->name)); + } else { + $this->Flash->error(__('Failed to import Accounts from Accounts Template: ' . $deAccountTemplate->name)); + } + + return $this->redirect(['action' => 'index']); + } + + /** + * Delete method + * + * @param string|null $id De Account Template id. + * @return \Cake\Http\Response|null Redirects to index. + * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found. + */ + public function delete($id = null) + { + $this->request->allowMethod(['post', 'delete']); + $deAccountTemplate = $this->DeAccountTemplates->get($id); + if ($this->DeAccountTemplates->delete($deAccountTemplate)) { + $this->Flash->success(__('The de ledger template has been deleted.')); + } else { + $this->Flash->error(__('The de ledger template could not be deleted. Please, try again.')); + } + + return $this->redirect(['action' => 'index']); + } +} diff --git a/src/Model/Entity/DeAccount.php b/src/Model/Entity/DeAccount.php new file mode 100644 index 0000000..afc3946 --- /dev/null +++ b/src/Model/Entity/DeAccount.php @@ -0,0 +1,57 @@ + + */ + protected array $_accessible = [ + 'account_number' => true, + 'parent_id' => true, + 'lft' => true, + 'rght' => true, + 'account_type_code' => true, + 'name' => true, + 'account_limit' => true, + 'can_credit' => true, + 'can_debit' => true, + 'parent_de_account' => true, + 'child_de_accounts' => true, + ]; + + /** + * @return string + */ + protected function _getAccountNumberAndName() + { + return $this->account_number . ' : ' . $this->name; + } +} diff --git a/src/Model/Entity/DeAccountStatement.php b/src/Model/Entity/DeAccountStatement.php new file mode 100644 index 0000000..6bc87d3 --- /dev/null +++ b/src/Model/Entity/DeAccountStatement.php @@ -0,0 +1,31 @@ + + */ + protected array $_accessible = [ + 'account_number' => true, + 'statement_date' => true, + 'closing_balance' => true, + ]; +} diff --git a/src/Model/Entity/DeAccountTemplate.php b/src/Model/Entity/DeAccountTemplate.php new file mode 100644 index 0000000..e44c6d4 --- /dev/null +++ b/src/Model/Entity/DeAccountTemplate.php @@ -0,0 +1,30 @@ + + */ + protected array $_accessible = [ + 'name' => true, + 'deleted' => true, + ]; +} diff --git a/src/Model/Entity/DeAccountType.php b/src/Model/Entity/DeAccountType.php new file mode 100644 index 0000000..d0c62cf --- /dev/null +++ b/src/Model/Entity/DeAccountType.php @@ -0,0 +1,28 @@ + + */ + protected array $_accessible = [ + 'name' => true, + ]; +} diff --git a/src/Model/Entity/DeBankAccountType.php b/src/Model/Entity/DeBankAccountType.php new file mode 100644 index 0000000..cd691be --- /dev/null +++ b/src/Model/Entity/DeBankAccountType.php @@ -0,0 +1,33 @@ + + */ + protected array $_accessible = [ + 'de_code' => true, + 'name' => true, + 'de_base_type' => true, + ]; +} diff --git a/src/Model/Entity/DeBaseType.php b/src/Model/Entity/DeBaseType.php new file mode 100644 index 0000000..814dba6 --- /dev/null +++ b/src/Model/Entity/DeBaseType.php @@ -0,0 +1,28 @@ + + */ + protected array $_accessible = [ + 'name' => true, + ]; +} diff --git a/src/Model/Entity/DeEntityType.php b/src/Model/Entity/DeEntityType.php new file mode 100644 index 0000000..9969579 --- /dev/null +++ b/src/Model/Entity/DeEntityType.php @@ -0,0 +1,28 @@ + + */ + protected array $_accessible = [ + 'name' => true, + ]; +} diff --git a/src/Model/Entity/DeExternalAccount.php b/src/Model/Entity/DeExternalAccount.php new file mode 100644 index 0000000..2b4b541 --- /dev/null +++ b/src/Model/Entity/DeExternalAccount.php @@ -0,0 +1,34 @@ + + */ + protected array $_accessible = [ + 'entity_type_code' => true, + 'created' => true, + 'related_model' => true, + 'related_model_fk' => true, + ]; +} diff --git a/src/Model/Entity/DeExternalAccountStatement.php b/src/Model/Entity/DeExternalAccountStatement.php new file mode 100644 index 0000000..5d2b7ff --- /dev/null +++ b/src/Model/Entity/DeExternalAccountStatement.php @@ -0,0 +1,33 @@ + + */ + protected array $_accessible = [ + 'closing_balance' => true, + 'total_credit' => true, + 'total_debit' => true, + ]; +} diff --git a/src/Model/Entity/DeJournal.php b/src/Model/Entity/DeJournal.php new file mode 100644 index 0000000..cfa665a --- /dev/null +++ b/src/Model/Entity/DeJournal.php @@ -0,0 +1,39 @@ + + */ + protected array $_accessible = [ + 'name' => true, + 'short_code' => true, + 'default_account_number_credit' => true, + 'default_account_number_debit' => true, + 'de_journal_type_code' => true, + 'de_journal_entries' => true, + ]; +} diff --git a/src/Model/Entity/DeJournalEntry.php b/src/Model/Entity/DeJournalEntry.php new file mode 100644 index 0000000..d2b7afc --- /dev/null +++ b/src/Model/Entity/DeJournalEntry.php @@ -0,0 +1,41 @@ + + */ + protected array $_accessible = [ + 'de_journal_id' => true, + 'user_id' => true, + 'created' => true, + 'notes' => true, + 'de_journal' => true, + 'user' => true, + 'de_journal_items' => true, + ]; +} diff --git a/src/Model/Entity/DeJournalItem.php b/src/Model/Entity/DeJournalItem.php new file mode 100644 index 0000000..40089e2 --- /dev/null +++ b/src/Model/Entity/DeJournalItem.php @@ -0,0 +1,43 @@ + + */ + protected array $_accessible = [ + 'account_number_credit' => true, + 'account_number_debit' => true, + 'external_account_number_credit' => true, + 'external_account_number_debit' => true, + 'de_journal_entry_id' => true, + 'amount' => true, + 'created' => true, + 'de_journal_entry' => true, + ]; +} diff --git a/src/Model/Entity/DeJournalType.php b/src/Model/Entity/DeJournalType.php new file mode 100644 index 0000000..6ee96f6 --- /dev/null +++ b/src/Model/Entity/DeJournalType.php @@ -0,0 +1,28 @@ + + */ + protected array $_accessible = [ + '*' => true, + 'de_journal_type_code' => false, + ]; +} diff --git a/src/Model/Entity/DeTemplatedAccount.php b/src/Model/Entity/DeTemplatedAccount.php new file mode 100644 index 0000000..6a2e0c2 --- /dev/null +++ b/src/Model/Entity/DeTemplatedAccount.php @@ -0,0 +1,51 @@ + + */ + protected array $_accessible = [ + 'account_number' => true, + 'account_template_id' => true, + 'parent_id' => true, + 'lft' => true, + 'rght' => true, + 'account_type_code' => true, + 'account_limit' => true, + 'can_credit' => true, + 'can_debit' => true, + 'name' => true, + 'parent_de_templated_account' => true, + 'child_de_templated_accounts' => true, + ]; +} diff --git a/src/Model/Entity/DeTemplatedLedger.php b/src/Model/Entity/DeTemplatedLedger.php new file mode 100644 index 0000000..6c1157e --- /dev/null +++ b/src/Model/Entity/DeTemplatedLedger.php @@ -0,0 +1,49 @@ + + */ + protected array $_accessible = [ + 'account_number' => true, + 'account_template_id' => true, + 'parent_id' => true, + 'lft' => true, + 'rght' => true, + 'account_type_code' => true, + 'limit' => true, + 'can_credit' => true, + 'can_debit' => true, + 'name' => true, + 'parent_de_templated_ledger' => true, + ]; +} diff --git a/src/Model/Entity/DeTransactionType.php b/src/Model/Entity/DeTransactionType.php new file mode 100644 index 0000000..a329c9d --- /dev/null +++ b/src/Model/Entity/DeTransactionType.php @@ -0,0 +1,28 @@ + + */ + protected array $_accessible = [ + 'name' => true, + ]; +} diff --git a/src/Model/Table/DeAccountStatementsTable.php b/src/Model/Table/DeAccountStatementsTable.php new file mode 100644 index 0000000..d966767 --- /dev/null +++ b/src/Model/Table/DeAccountStatementsTable.php @@ -0,0 +1,116 @@ + newEntities(array $data, array $options = []) + * @method DeAccountStatement get(mixed $primaryKey, array|string $finder = 'all', CacheInterface|string|null $cache = null, Closure|string|null $cacheKey = null, mixed ...$args) + * @method DeAccountStatement findOrCreate($search, ?callable $callback = null, array $options = []) + * @method DeAccountStatement patchEntity(EntityInterface $entity, array $data, array $options = []) + * @method array patchEntities(iterable $entities, array $data, array $options = []) + * @method DeAccountStatement|false save(EntityInterface $entity, array $options = []) + * @method DeAccountStatement saveOrFail(EntityInterface $entity, array $options = []) + * @method iterable|ResultSetInterface|false saveMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface saveManyOrFail(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface|false deleteMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface deleteManyOrFail(iterable $entities, array $options = []) + */ +class DeAccountStatementsTable extends Table +{ + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + + $this->setTable('de_account_statements'); + $this->setDisplayField('account_statement_id'); + $this->setPrimaryKey('account_statement_id'); + + $this->belongsTo('DeAccounts', [ + 'bindingKey' => 'account_number', + 'foreignKey' => 'account_number', + 'className' => 'CakeAccounting.DeAccounts', + ]); + } + + /** + * Default validation rules. + * + * @param Validator $validator Validator instance. + * @return Validator + */ + public function validationDefault(Validator $validator): Validator + { + $validator + ->uuid('account_statement_id'); + + $validator + ->date('statement_date') + ->requirePresence('statement_date', 'create'); + + $validator + ->decimal('closing_balance') + ->requirePresence('closing_balance', 'create') + ->notEmptyString('closing_balance'); + + return $validator; + } + + /** + * Returns a rules checker object that will be used for validating + * application integrity. + * + * @param RulesChecker $rules The rules object to be modified. + * @return RulesChecker + */ + public function buildRules(RulesChecker $rules): RulesChecker + { + $rules->add($rules->existsIn(['account_number'], 'DeAccounts'), ['errorField' => 'account_number']); + + return $rules; + } + + /** + * @param DeAccount $deAccount + * + * @return \Cake\Datasource\EntityInterface|false + */ + public function createFirstAccountSatement(DeAccount $deAccount) + { + $statementDate = date('Y') . '-' . date('m') . '-01'; + $data = [ + 'account_number' => $deAccount->account_number, + 'statement_date' => $statementDate, + 'closing_balance' => 0, + ]; + $deAccountStatement = $this->newEntity($data); + if ($deAccountStatement->getErrors()) { + Log::debug(print_r('$deAccountStatement->getErrors() creating new/first blank statement', true)); + Log::debug(print_r($deAccountStatement->getErrors(), true)); + } + + return $this->save($deAccountStatement); + } +} diff --git a/src/Model/Table/DeAccountTemplatesTable.php b/src/Model/Table/DeAccountTemplatesTable.php new file mode 100644 index 0000000..a9725a1 --- /dev/null +++ b/src/Model/Table/DeAccountTemplatesTable.php @@ -0,0 +1,75 @@ + newEntities(array $data, array $options = []) + * @method DeAccountTemplate get(mixed $primaryKey, array|string $finder = 'all', CacheInterface|string|null $cache = null, Closure|string|null $cacheKey = null, mixed ...$args) + * @method DeAccountTemplate findOrCreate($search, ?callable $callback = null, array $options = []) + * @method DeAccountTemplate patchEntity(EntityInterface $entity, array $data, array $options = []) + * @method array patchEntities(iterable $entities, array $data, array $options = []) + * @method DeAccountTemplate|false save(EntityInterface $entity, array $options = []) + * @method DeAccountTemplate saveOrFail(EntityInterface $entity, array $options = []) + * @method iterable|ResultSetInterface|false saveMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface saveManyOrFail(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface|false deleteMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface deleteManyOrFail(iterable $entities, array $options = []) + */ +class DeAccountTemplatesTable extends Table +{ + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + + $this->setTable('de_account_templates'); + $this->setDisplayField('name'); + $this->setPrimaryKey('id'); + + $this->hasMany('DeTemplatedAccounts', [ + 'foreignKey' => 'account_template_id', + 'className' => 'CakeAccounting.DeTemplatedAccounts', + ]); + } + + /** + * Default validation rules. + * + * @param Validator $validator Validator instance. + * @return Validator + */ + public function validationDefault(Validator $validator): Validator + { + $validator + ->scalar('name') + ->maxLength('name', 45) + ->requirePresence('name', 'create') + ->notEmptyString('name'); + + $validator + ->dateTime('deleted') + ->allowEmptyDateTime('deleted'); + + return $validator; + } +} diff --git a/src/Model/Table/DeAccountTypesTable.php b/src/Model/Table/DeAccountTypesTable.php new file mode 100644 index 0000000..152931c --- /dev/null +++ b/src/Model/Table/DeAccountTypesTable.php @@ -0,0 +1,61 @@ + newEntities(array $data, array $options = []) + * @method \CakeAccounting\Model\Entity\DeAccountType get(mixed $primaryKey, array|string $finder = 'all', \Psr\SimpleCache\CacheInterface|string|null $cache = null, \Closure|string|null $cacheKey = null, mixed ...$args) + * @method \CakeAccounting\Model\Entity\DeAccountType findOrCreate($search, ?callable $callback = null, array $options = []) + * @method \CakeAccounting\Model\Entity\DeAccountType patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = []) + * @method array<\CakeAccounting\Model\Entity\DeAccountType> patchEntities(iterable $entities, array $data, array $options = []) + * @method \CakeAccounting\Model\Entity\DeAccountType|false save(\Cake\Datasource\EntityInterface $entity, array $options = []) + * @method \CakeAccounting\Model\Entity\DeAccountType saveOrFail(\Cake\Datasource\EntityInterface $entity, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeAccountType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeAccountType>|false saveMany(iterable $entities, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeAccountType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeAccountType> saveManyOrFail(iterable $entities, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeAccountType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeAccountType>|false deleteMany(iterable $entities, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeAccountType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeAccountType> deleteManyOrFail(iterable $entities, array $options = []) + */ +class DeAccountTypesTable extends Table +{ + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + + $this->setTable('de_account_types'); + $this->setDisplayField('name'); + $this->setPrimaryKey('account_type_code'); + } + + /** + * Default validation rules. + * + * @param \Cake\Validation\Validator $validator Validator instance. + * @return \Cake\Validation\Validator + */ + public function validationDefault(Validator $validator): Validator + { + $validator + ->scalar('name') + ->maxLength('name', 45) + ->requirePresence('name', 'create') + ->notEmptyString('name'); + + return $validator; + } +} diff --git a/src/Model/Table/DeAccountsTable.php b/src/Model/Table/DeAccountsTable.php new file mode 100644 index 0000000..971a83c --- /dev/null +++ b/src/Model/Table/DeAccountsTable.php @@ -0,0 +1,253 @@ + newEntities(array $data, array $options = []) + * @method DeAccount get(mixed $primaryKey, array|string $finder = 'all', CacheInterface|string|null $cache = null, Closure|string|null $cacheKey = null, mixed ...$args) + * @method DeAccount findOrCreate($search, ?callable $callback = null, array $options = []) + * @method DeAccount patchEntity(EntityInterface $entity, array $data, array $options = []) + * @method array patchEntities(iterable $entities, array $data, array $options = []) + * @method DeAccount|false save(EntityInterface $entity, array $options = []) + * @method DeAccount saveOrFail(EntityInterface $entity, array $options = []) + * @method iterable|ResultSetInterface|false saveMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface saveManyOrFail(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface|false deleteMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface deleteManyOrFail(iterable $entities, array $options = []) + * + * @mixin TreeBehavior + */ +class DeAccountsTable extends Table +{ + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + + $this->setTable('de_accounts'); + $this->setDisplayField('name'); + $this->setPrimaryKey('id'); + + $this->addBehavior('Tree'); + + $this->belongsTo('DeAccountTypes', [ + 'foreignKey' => 'account_type_code', + 'bindingKey' => 'account_type_code', + 'className' => 'CakeAccounting.DeAccountTypes', + ]); + $this->belongsTo('ParentDeAccounts', [ + 'foreignKey' => 'parent_id', + 'className' => 'CakeAccounting.DeAccounts', + ]); + $this->hasMany('ChildDeAccounts', [ + 'foreignKey' => 'parent_id', + 'className' => 'CakeAccounting.DeAccounts', + ]); + $this->hasMany('DeAccountStatements', [ + 'foreignKey' => 'account_number', + 'className' => 'CakeAccounting.DeAccountStatements', + ]); + $this->hasMany('DebitDeJournalItems', [ + 'bindingKey' => 'account_number', + 'foreignKey' => 'debit_account_number', + 'className' => 'CakeAccounting.DeJournalItems', + ]); + $this->hasMany('CreditDeJournalItems', [ + 'bindingKey' => 'account_number', + 'foreignKey' => 'credit_account_number', + 'className' => 'CakeAccounting.DeJournalItems', + ]); + } + + /** + * Default validation rules. + * + * @param Validator $validator Validator instance. + * @return Validator + */ + public function validationDefault(Validator $validator): Validator + { + $validator + ->integer('account_number') + ->requirePresence('account_number', 'create') + ->notEmptyString('account_number') + ->add('account_number', 'unique', ['rule' => 'validateUnique', 'provider' => 'table']); + + $validator + ->integer('parent_id') + ->allowEmptyString('parent_id'); + + $validator + ->scalar('account_type_code') + ->maxLength('account_type_code', 2) + ->requirePresence('account_type_code', 'create') + ->notEmptyString('account_type_code'); + + $validator + ->scalar('name') + ->maxLength('name', 255) + ->requirePresence('name', 'create') + ->notEmptyString('name'); + + $validator + ->integer('account_limit') + ->allowEmptyString('account_limit'); + + $validator + ->boolean('can_credit') + ->notEmptyString('can_credit'); + + $validator + ->boolean('can_debit') + ->notEmptyString('can_debit'); + + return $validator; + } + + /** + * Returns a rules checker object that will be used for validating + * application integrity. + * + * @param RulesChecker $rules The rules object to be modified. + * + * @return RulesChecker + */ + public function buildRules(RulesChecker $rules): RulesChecker + { + $rules->add($rules->isUnique(['account_number']), ['errorField' => 'account_number']); + $rules->add($rules->existsIn(['parent_id'], 'ParentDeAccounts'), ['errorField' => 'parent_id']); + $rules->add($rules->existsIn(['account_type_code'], 'DeAccountTypes'), ['errorField' => 'account_type_code']); + + return $rules; + } + + /** + * @param EventInterface $event event object + * @param DeAccount|EntityInterface $entity entity just saved + * @param ArrayObject $options save options + * + * @return void + */ + public function afterSave(EventInterface $event, DeAccount|EntityInterface $entity, ArrayObject $options): void + { + if ($entity->isNew()) { + $firstBlankStatement = $this->DeAccountStatements->find()->where(['account_number' => $entity->account_number])->first(); + if (!isset($firstBlankStatement)) { + $firstBlankStatement = $this->DeAccountStatements->createFirstAccountSatement($entity); + } + } + } + + /** + * @param Query $query The query object to be modified. + * @param array $options finder options - not used in this custom finder + * + * @return Query + */ + public function findAccountTreeList(Query $query, array $options): Query + { + return $query->find('treeList', keyPath: 'account_number', valuePath: 'account_number_and_name'); + } + + /** + * @param Query $query The query object to be modified. + * @param array $options finder options - not used in this custom finder + * + * @return Query + */ + public function findByAccountNumber(Query $query, array $options): Query + { + if (!array_key_exists('account_number', $options) || !$options['account_number']) { + throw new CakeException('Account not found. Account Number not provided.', 404); + } + return $query->where(['DeAccounts.account_number' => $options['account_number']]); + } + + /** + * @param DeTemplatedAccount $deTemplatedAccount + * + * @return DeAccount|false|void + */ + public function importAccountFromTemplatedAccount(DeTemplatedAccount $deTemplatedAccount): DeAccount|bool + { + $templatedAccountsTable = TableRegistry::getTableLocator()->get('CakeAccounting.DeTemplatedAccounts'); + + $tmpData = $deTemplatedAccount->toArray(); + if ($deTemplatedAccount->parent_id) { + Log::debug('parent id set'); + $tmpOldParent = $templatedAccountsTable->get($deTemplatedAccount->parent_id); + $tmpNewParent = $this->find()->where(['account_number' => $tmpOldParent->account_number])->first(); + if (!isset($tmpNewParent)) { + Log::debug('new parent not set - try to import the old parent now'); + $parentImportResult = $this->importAccountFromTemplatedAccount($tmpOldParent); + Log::debug(print_r('$parentImportResult', true)); + Log::debug(print_r($parentImportResult, true)); + } + $tmpNewParent = $this->find()->where(['account_number' => $tmpOldParent->account_number])->first(); + + if (!isset($tmpNewParent)) { + Log::debug('failed to get new parent for a second time'); + + return false; + } + $tmpData['parent_id'] = $tmpNewParent->id; + } + $entity = $this->newEntity($tmpData); + $saveResult = $this->save($entity); + if (!$saveResult) { + Log::debug(print_r('$entity->getErrors()', true)); + Log::debug(print_r($entity->getErrors(), true)); + } + if (!isset($deTemplatedAccount->children)) { + $deTemplatedAccount->children = $templatedAccountsTable + ->find('children', for: $deTemplatedAccount->id) + ->find('threaded') + ->toArray(); + } + if (!isset($deTemplatedAccount->children) || !$deTemplatedAccount->children) { + return $saveResult; + } + $allResult = true; + foreach ($deTemplatedAccount->children as $child) { + if (!$this->importAccountFromTemplatedAccount($child)) { + $allResult = false; + } + } + + return $allResult; + } + +} diff --git a/src/Model/Table/DeBankAccountTypesTable.php b/src/Model/Table/DeBankAccountTypesTable.php new file mode 100644 index 0000000..c52b559 --- /dev/null +++ b/src/Model/Table/DeBankAccountTypesTable.php @@ -0,0 +1,67 @@ + newEntities(array $data, array $options = []) + * @method \CakeAccounting\Model\Entity\DeBankAccountType get(mixed $primaryKey, array|string $finder = 'all', \Psr\SimpleCache\CacheInterface|string|null $cache = null, \Closure|string|null $cacheKey = null, mixed ...$args) + * @method \CakeAccounting\Model\Entity\DeBankAccountType findOrCreate($search, ?callable $callback = null, array $options = []) + * @method \CakeAccounting\Model\Entity\DeBankAccountType patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = []) + * @method array<\CakeAccounting\Model\Entity\DeBankAccountType> patchEntities(iterable $entities, array $data, array $options = []) + * @method \CakeAccounting\Model\Entity\DeBankAccountType|false save(\Cake\Datasource\EntityInterface $entity, array $options = []) + * @method \CakeAccounting\Model\Entity\DeBankAccountType saveOrFail(\Cake\Datasource\EntityInterface $entity, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeBankAccountType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeBankAccountType>|false saveMany(iterable $entities, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeBankAccountType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeBankAccountType> saveManyOrFail(iterable $entities, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeBankAccountType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeBankAccountType>|false deleteMany(iterable $entities, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeBankAccountType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeBankAccountType> deleteManyOrFail(iterable $entities, array $options = []) + */ +class DeBankAccountTypesTable extends Table +{ + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + + $this->setTable('de_bank_account_types'); + $this->setDisplayField('name'); + $this->setPrimaryKey('bank_account_type_code'); + } + + /** + * Default validation rules. + * + * @param \Cake\Validation\Validator $validator Validator instance. + * @return \Cake\Validation\Validator + */ + public function validationDefault(Validator $validator): Validator + { + $validator + ->scalar('de_code') + ->maxLength('de_code', 2) + ->requirePresence('de_code', 'create') + ->notEmptyString('de_code'); + + $validator + ->scalar('name') + ->maxLength('name', 45) + ->requirePresence('name', 'create') + ->notEmptyString('name'); + + return $validator; + } +} diff --git a/src/Model/Table/DeBaseTypesTable.php b/src/Model/Table/DeBaseTypesTable.php new file mode 100644 index 0000000..98367a4 --- /dev/null +++ b/src/Model/Table/DeBaseTypesTable.php @@ -0,0 +1,61 @@ + newEntities(array $data, array $options = []) + * @method \CakeAccounting\Model\Entity\DeBaseType get(mixed $primaryKey, array|string $finder = 'all', \Psr\SimpleCache\CacheInterface|string|null $cache = null, \Closure|string|null $cacheKey = null, mixed ...$args) + * @method \CakeAccounting\Model\Entity\DeBaseType findOrCreate($search, ?callable $callback = null, array $options = []) + * @method \CakeAccounting\Model\Entity\DeBaseType patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = []) + * @method array<\CakeAccounting\Model\Entity\DeBaseType> patchEntities(iterable $entities, array $data, array $options = []) + * @method \CakeAccounting\Model\Entity\DeBaseType|false save(\Cake\Datasource\EntityInterface $entity, array $options = []) + * @method \CakeAccounting\Model\Entity\DeBaseType saveOrFail(\Cake\Datasource\EntityInterface $entity, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeBaseType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeBaseType>|false saveMany(iterable $entities, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeBaseType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeBaseType> saveManyOrFail(iterable $entities, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeBaseType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeBaseType>|false deleteMany(iterable $entities, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeBaseType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeBaseType> deleteManyOrFail(iterable $entities, array $options = []) + */ +class DeBaseTypesTable extends Table +{ + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + + $this->setTable('de_base_types'); + $this->setDisplayField('name'); + $this->setPrimaryKey('de_code'); + } + + /** + * Default validation rules. + * + * @param \Cake\Validation\Validator $validator Validator instance. + * @return \Cake\Validation\Validator + */ + public function validationDefault(Validator $validator): Validator + { + $validator + ->scalar('name') + ->maxLength('name', 45) + ->requirePresence('name', 'create') + ->notEmptyString('name'); + + return $validator; + } +} diff --git a/src/Model/Table/DeEntityTypesTable.php b/src/Model/Table/DeEntityTypesTable.php new file mode 100644 index 0000000..f7ab5f4 --- /dev/null +++ b/src/Model/Table/DeEntityTypesTable.php @@ -0,0 +1,61 @@ + newEntities(array $data, array $options = []) + * @method \CakeAccounting\Model\Entity\DeEntityType get(mixed $primaryKey, array|string $finder = 'all', \Psr\SimpleCache\CacheInterface|string|null $cache = null, \Closure|string|null $cacheKey = null, mixed ...$args) + * @method \CakeAccounting\Model\Entity\DeEntityType findOrCreate($search, ?callable $callback = null, array $options = []) + * @method \CakeAccounting\Model\Entity\DeEntityType patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = []) + * @method array<\CakeAccounting\Model\Entity\DeEntityType> patchEntities(iterable $entities, array $data, array $options = []) + * @method \CakeAccounting\Model\Entity\DeEntityType|false save(\Cake\Datasource\EntityInterface $entity, array $options = []) + * @method \CakeAccounting\Model\Entity\DeEntityType saveOrFail(\Cake\Datasource\EntityInterface $entity, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeEntityType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeEntityType>|false saveMany(iterable $entities, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeEntityType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeEntityType> saveManyOrFail(iterable $entities, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeEntityType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeEntityType>|false deleteMany(iterable $entities, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeEntityType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeEntityType> deleteManyOrFail(iterable $entities, array $options = []) + */ +class DeEntityTypesTable extends Table +{ + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + + $this->setTable('de_entity_types'); + $this->setDisplayField('name'); + $this->setPrimaryKey('entity_type_code'); + } + + /** + * Default validation rules. + * + * @param \Cake\Validation\Validator $validator Validator instance. + * @return \Cake\Validation\Validator + */ + public function validationDefault(Validator $validator): Validator + { + $validator + ->scalar('name') + ->maxLength('name', 45) + ->requirePresence('name', 'create') + ->notEmptyString('name'); + + return $validator; + } +} diff --git a/src/Model/Table/DeExternalAccountStatementsTable.php b/src/Model/Table/DeExternalAccountStatementsTable.php new file mode 100644 index 0000000..0cb7aec --- /dev/null +++ b/src/Model/Table/DeExternalAccountStatementsTable.php @@ -0,0 +1,84 @@ + newEntities(array $data, array $options = []) + * @method DeExternalAccountStatement get(mixed $primaryKey, array|string $finder = 'all', CacheInterface|string|null $cache = null, Closure|string|null $cacheKey = null, mixed ...$args) + * @method DeExternalAccountStatement findOrCreate($search, ?callable $callback = null, array $options = []) + * @method DeExternalAccountStatement patchEntity(EntityInterface $entity, array $data, array $options = []) + * @method array patchEntities(iterable $entities, array $data, array $options = []) + * @method DeExternalAccountStatement|false save(EntityInterface $entity, array $options = []) + * @method DeExternalAccountStatement saveOrFail(EntityInterface $entity, array $options = []) + * @method iterable|ResultSetInterface|false saveMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface saveManyOrFail(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface|false deleteMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface deleteManyOrFail(iterable $entities, array $options = []) + */ +class DeExternalAccountStatementsTable extends Table +{ + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + + $this->setTable('de_external_account_statements'); + $this->setDisplayField('external_account_statement_id'); + $this->setPrimaryKey('external_account_statement_id'); + + $this->belongsTo('DeExternalAccounts', [ + 'className' => 'CakeAccounting.DeExternalAccounts', + 'foreignKey' => 'external_account_number', + 'bindingKey' => 'external_account_number', + ]); + } + + /** + * Default validation rules. + * + * @param Validator $validator Validator instance. + * @return Validator + */ + public function validationDefault(Validator $validator): Validator + { + $validator + ->uuid('external_account_statement_id'); + + $validator + ->decimal('closing_balance') + ->requirePresence('closing_balance', 'create') + ->notEmptyString('closing_balance'); + + $validator + ->decimal('total_credit') + ->requirePresence('total_credit', 'create') + ->notEmptyString('total_credit'); + + $validator + ->decimal('total_debit') + ->requirePresence('total_debit', 'create') + ->notEmptyString('total_debit'); + + return $validator; + } +} diff --git a/src/Model/Table/DeExternalAccountsTable.php b/src/Model/Table/DeExternalAccountsTable.php new file mode 100644 index 0000000..1af5e5f --- /dev/null +++ b/src/Model/Table/DeExternalAccountsTable.php @@ -0,0 +1,91 @@ + newEntities(array $data, array $options = []) + * @method DeExternalAccount get(mixed $primaryKey, array|string $finder = 'all', CacheInterface|string|null $cache = null, Closure|string|null $cacheKey = null, mixed ...$args) + * @method DeExternalAccount findOrCreate($search, ?callable $callback = null, array $options = []) + * @method DeExternalAccount patchEntity(EntityInterface $entity, array $data, array $options = []) + * @method array patchEntities(iterable $entities, array $data, array $options = []) + * @method DeExternalAccount|false save(EntityInterface $entity, array $options = []) + * @method DeExternalAccount saveOrFail(EntityInterface $entity, array $options = []) + * @method iterable|ResultSetInterface|false saveMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface saveManyOrFail(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface|false deleteMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface deleteManyOrFail(iterable $entities, array $options = []) + * + * @mixin TimestampBehavior + */ +class DeExternalAccountsTable extends Table +{ + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + + $this->setTable('de_external_accounts'); + $this->setDisplayField('entity_type_code'); + $this->setPrimaryKey('external_account_number'); + + $this->addBehavior('Timestamp'); + + $this->belongsTo('DeEntityTypes', [ + 'className' => 'CakeAccounting.DeEntityTypes', + 'foreignKey' => 'entity_type_code', + 'bindingKey' => 'entity_type_code', + ]); + $this->hasMany('DeExternalAccountStatements', [ + 'className' => 'CakeAccounting.DeExternalAccountStatements', + 'foreignKey' => 'external_account_number', + 'bindingKey' => 'external_account_number', + ]); + } + + /** + * Default validation rules. + * + * @param Validator $validator Validator instance. + * @return Validator + */ + public function validationDefault(Validator $validator): Validator + { + $validator + ->scalar('entity_type_code') + ->maxLength('entity_type_code', 2) + ->requirePresence('entity_type_code', 'create') + ->notEmptyString('entity_type_code'); + + $validator + ->scalar('related_model') + ->maxLength('related_model', 255) + ->allowEmptyString('related_model'); + + $validator + ->integer('related_model_fk') + ->allowEmptyString('related_model_fk'); + + return $validator; + } +} diff --git a/src/Model/Table/DeInternalAccountTypesTable.php b/src/Model/Table/DeInternalAccountTypesTable.php new file mode 100644 index 0000000..a983621 --- /dev/null +++ b/src/Model/Table/DeInternalAccountTypesTable.php @@ -0,0 +1,66 @@ + newEntities(array $data, array $options = []) + * @method DeInternalAccountType get(mixed $primaryKey, array|string $finder = 'all', CacheInterface|string|null $cache = null, Closure|string|null $cacheKey = null, mixed ...$args) + * @method DeInternalAccountType findOrCreate($search, ?callable $callback = null, array $options = []) + * @method DeInternalAccountType patchEntity(EntityInterface $entity, array $data, array $options = []) + * @method array patchEntities(iterable $entities, array $data, array $options = []) + * @method DeInternalAccountType|false save(EntityInterface $entity, array $options = []) + * @method DeInternalAccountType saveOrFail(EntityInterface $entity, array $options = []) + * @method iterable|ResultSetInterface|false saveMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface saveManyOrFail(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface|false deleteMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface deleteManyOrFail(iterable $entities, array $options = []) + */ +class DeInternalAccountTypesTable extends Table +{ + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + + $this->setTable('de_internal_account_types'); + $this->setDisplayField('name'); + $this->setPrimaryKey('account_type_code'); + } + + /** + * Default validation rules. + * + * @param Validator $validator Validator instance. + * @return Validator + */ + public function validationDefault(Validator $validator): Validator + { + $validator + ->scalar('name') + ->maxLength('name', 45) + ->requirePresence('name', 'create') + ->notEmptyString('name'); + + return $validator; + } +} diff --git a/src/Model/Table/DeJournalEntriesTable.php b/src/Model/Table/DeJournalEntriesTable.php new file mode 100644 index 0000000..edb997a --- /dev/null +++ b/src/Model/Table/DeJournalEntriesTable.php @@ -0,0 +1,113 @@ + newEntities(array $data, array $options = []) + * @method DeJournalEntry get(mixed $primaryKey, array|string $finder = 'all', CacheInterface|string|null $cache = null, Closure|string|null $cacheKey = null, mixed ...$args) + * @method DeJournalEntry findOrCreate($search, ?callable $callback = null, array $options = []) + * @method DeJournalEntry patchEntity(EntityInterface $entity, array $data, array $options = []) + * @method array patchEntities(iterable $entities, array $data, array $options = []) + * @method DeJournalEntry|false save(EntityInterface $entity, array $options = []) + * @method DeJournalEntry saveOrFail(EntityInterface $entity, array $options = []) + * @method iterable|ResultSetInterface|false saveMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface saveManyOrFail(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface|false deleteMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface deleteManyOrFail(iterable $entities, array $options = []) + * + * @mixin TimestampBehavior + */ +class DeJournalEntriesTable extends Table +{ + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + + $this->setTable('de_journal_entries'); + $this->setDisplayField('id'); + $this->setPrimaryKey('id'); + + $this->addBehavior('Timestamp'); + + $this->belongsTo('DeJournals', [ + 'foreignKey' => 'de_journal_id', + 'joinType' => 'INNER', + 'className' => 'CakeAccounting.DeJournals', + ]); +// $this->belongsTo('Users', [ +// 'foreignKey' => 'user_id', +// 'className' => 'CakeAccounting.Users', +// ]); + $this->hasMany('DeJournalItems', [ + 'foreignKey' => 'de_journal_entry_id', + 'className' => 'CakeAccounting.DeJournalItems', + ]); + } + + /** + * Default validation rules. + * + * @param Validator $validator Validator instance. + * @return Validator + */ + public function validationDefault(Validator $validator): Validator + { + $validator + ->integer('de_journal_id') + ->requirePresence('de_journal_id', 'create') + ->notEmptyString('de_journal_id'); + + $validator + ->integer('user_id') + ->allowEmptyString('user_id'); + + $validator + ->scalar('notes') + ->allowEmptyString('notes'); + + return $validator; + } + + /** + * Returns a rules checker object that will be used for validating + * application integrity. + * + * @param RulesChecker $rules The rules object to be modified. + * @return RulesChecker + */ + public function buildRules(RulesChecker $rules): RulesChecker + { + $rules->add($rules->existsIn(['de_journal_id'], 'DeJournals'), ['errorField' => 'de_journal_id']); +// $rules->add($rules->existsIn(['user_id'], 'Users'), ['errorField' => '1']); + + return $rules; + } +} diff --git a/src/Model/Table/DeJournalItemsTable.php b/src/Model/Table/DeJournalItemsTable.php new file mode 100644 index 0000000..1eda13a --- /dev/null +++ b/src/Model/Table/DeJournalItemsTable.php @@ -0,0 +1,151 @@ + newEntities(array $data, array $options = []) + * @method DeJournalItem get(mixed $primaryKey, array|string $finder = 'all', CacheInterface|string|null $cache = null, Closure|string|null $cacheKey = null, mixed ...$args) + * @method DeJournalItem findOrCreate($search, ?callable $callback = null, array $options = []) + * @method DeJournalItem patchEntity(EntityInterface $entity, array $data, array $options = []) + * @method array patchEntities(iterable $entities, array $data, array $options = []) + * @method DeJournalItem|false save(EntityInterface $entity, array $options = []) + * @method DeJournalItem saveOrFail(EntityInterface $entity, array $options = []) + * @method iterable|ResultSetInterface|false saveMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface saveManyOrFail(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface|false deleteMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface deleteManyOrFail(iterable $entities, array $options = []) + * + * @mixin TimestampBehavior + */ +class DeJournalItemsTable extends Table +{ + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + + $this->setTable('de_journal_items'); + $this->setDisplayField('id'); + $this->setPrimaryKey('id'); + + $this->addBehavior('Timestamp'); + + $this->belongsTo('DeJournalEntries', [ + 'foreignKey' => 'de_journal_entry_id', + 'joinType' => 'INNER', + 'className' => 'CakeAccounting.DeJournalEntries', + ]); + $this->belongsTo('CreditDeAccounts', [ + 'foreignKey' => 'account_number_credit', + 'bindingKey' => 'account_number', + 'joinType' => 'INNER', + 'className' => 'CakeAccounting.DeAccounts', + ]); + $this->belongsTo('DebitDeAccounts', [ + 'foreignKey' => 'account_number_debit', + 'bindingKey' => 'account_number', + 'joinType' => 'INNER', + 'className' => 'CakeAccounting.DeAccounts', + ]); + $this->belongsTo('CreditDeExternalAccounts', [ + 'foreignKey' => 'external_account_number_credit', + 'bindingKey' => 'external_account_number', + 'className' => 'CakeAccounting.DeExternalAccounts', + ]); + $this->belongsTo('DebitDeExternalAccounts', [ + 'foreignKey' => 'external_account_number_debit', + 'bindingKey' => 'external_account_number', + 'className' => 'CakeAccounting.DeExternalAccounts', + ]); + } + + /** + * Default validation rules. + * + * @param Validator $validator Validator instance. + * @return Validator + */ + public function validationDefault(Validator $validator): Validator + { + $validator + ->integer('account_number_credit') + ->requirePresence('account_number_credit', 'create') + ->notEmptyString('account_number_credit'); + + $validator + ->integer('account_number_debit') + ->requirePresence('account_number_debit', 'create') + ->notEmptyString('account_number_debit'); + + $validator + ->uuid('external_account_number_credit') + ->allowEmptyString('external_account_number_credit'); + + $validator + ->uuid('external_account_number_debit') + ->allowEmptyString('external_account_number_debit'); + + $validator + ->integer('de_journal_entry_id') + ->notEmptyString('de_journal_entry_id'); + + $validator + ->decimal('amount') + ->requirePresence('amount', 'create') + ->notEmptyString('amount'); + + return $validator; + } + + /** + * Returns a rules checker object that will be used for validating + * application integrity. + * + * @param RulesChecker $rules The rules object to be modified. + * @return RulesChecker + */ + public function buildRules(RulesChecker $rules): RulesChecker + { + $rules->add($rules->existsIn(['de_journal_entry_id'], 'DeJournalEntries'), ['errorField' => 'de_journal_entry_id']); + $rules->add($rules->existsIn(['account_number_credit'], 'CreditDeAccounts'), ['errorField' => 'account_number_credit']); + $rules->add($rules->existsIn(['account_number_debit'], 'DebitDeAccounts'), ['errorField' => 'account_number_debit']); + $rules->add($rules->existsIn(['external_account_number_credit'], 'CreditDeExternalAccounts'), ['errorField' => 'external_account_number_credit']); + $rules->add($rules->existsIn(['external_account_number_debit'], 'DebitDeExternalAccounts'), ['errorField' => 'external_account_number_debit']); + + return $rules; + } + + public function findDebitsForAccountNumber(Query $query, int $accountNumber): Query + { + return $query->where(['account_number_debit' => $accountNumber]); + } + public function findCreditsForAccountNumber(Query $query, int $accountNumber): Query + { + return $query->where(['account_number_credit' => $accountNumber]); + } +} diff --git a/src/Model/Table/DeJournalTypesTable.php b/src/Model/Table/DeJournalTypesTable.php new file mode 100644 index 0000000..c9e33e5 --- /dev/null +++ b/src/Model/Table/DeJournalTypesTable.php @@ -0,0 +1,49 @@ + newEntities(array $data, array $options = []) + * @method DeJournalType get(mixed $primaryKey, array|string $finder = 'all', CacheInterface|string|null $cache = null, Closure|string|null $cacheKey = null, mixed ...$args) + * @method DeJournalType findOrCreate($search, ?callable $callback = null, array $options = []) + * @method DeJournalType patchEntity(EntityInterface $entity, array $data, array $options = []) + * @method array patchEntities(iterable $entities, array $data, array $options = []) + * @method DeJournalType|false save(EntityInterface $entity, array $options = []) + * @method DeJournalType saveOrFail(EntityInterface $entity, array $options = []) + * @method iterable|ResultSetInterface|false saveMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface saveManyOrFail(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface|false deleteMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface deleteManyOrFail(iterable $entities, array $options = []) + */ +class DeJournalTypesTable extends Table +{ + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + + $this->setTable('de_journal_types'); + $this->setDisplayField('de_journal_type_code'); + $this->setPrimaryKey('de_journal_type_code'); + } +} diff --git a/src/Model/Table/DeJournalsTable.php b/src/Model/Table/DeJournalsTable.php new file mode 100644 index 0000000..3f854a8 --- /dev/null +++ b/src/Model/Table/DeJournalsTable.php @@ -0,0 +1,99 @@ + newEntities(array $data, array $options = []) + * @method DeJournal get(mixed $primaryKey, array|string $finder = 'all', CacheInterface|string|null $cache = null, Closure|string|null $cacheKey = null, mixed ...$args) + * @method DeJournal findOrCreate($search, ?callable $callback = null, array $options = []) + * @method DeJournal patchEntity(EntityInterface $entity, array $data, array $options = []) + * @method array patchEntities(iterable $entities, array $data, array $options = []) + * @method DeJournal|false save(EntityInterface $entity, array $options = []) + * @method DeJournal saveOrFail(EntityInterface $entity, array $options = []) + * @method iterable|ResultSetInterface|false saveMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface saveManyOrFail(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface|false deleteMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface deleteManyOrFail(iterable $entities, array $options = []) + */ +class DeJournalsTable extends Table +{ + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + + $this->setTable('de_journals'); + $this->setDisplayField('name'); + $this->setPrimaryKey('id'); + + $this->hasMany('DeJournalEntries', [ + 'foreignKey' => 'de_journal_id', + 'className' => 'CakeAccounting.DeJournalEntries', + ]); + $this->belongsTo('DeJournalTypes', [ + 'foreignKey' => 'de_journal_type_code', + 'bindingKey' => 'de_journal_type_code', + 'className' => 'CakeAccounting.DeJournalTypes', + ]); + } + + /** + * Default validation rules. + * + * @param Validator $validator Validator instance. + * @return Validator + */ + public function validationDefault(Validator $validator): Validator + { + $validator + ->scalar('name') + ->maxLength('name', 45) + ->requirePresence('name', 'create') + ->notEmptyString('name'); + + $validator + ->scalar('short_code') + ->maxLength('short_code', 5) + ->requirePresence('short_code', 'create') + ->notEmptyString('short_code'); + + $validator + ->integer('default_account_number_credit') + ->allowEmptyString('default_account_number_credit'); + + $validator + ->integer('default_account_number_debit') + ->allowEmptyString('default_account_number_debit'); + + $validator + ->scalar('de_journal_type_code') + ->maxLength('de_journal_type_code', 5) + ->requirePresence('de_journal_type_code', 'create') + ->notEmptyString('de_journal_type_code'); + + return $validator; + } +} diff --git a/src/Model/Table/DeTemplatedAccountsTable.php b/src/Model/Table/DeTemplatedAccountsTable.php new file mode 100644 index 0000000..8a7a8f2 --- /dev/null +++ b/src/Model/Table/DeTemplatedAccountsTable.php @@ -0,0 +1,140 @@ + newEntities(array $data, array $options = []) + * @method DeTemplatedAccount get(mixed $primaryKey, array|string $finder = 'all', CacheInterface|string|null $cache = null, Closure|string|null $cacheKey = null, mixed ...$args) + * @method DeTemplatedAccount findOrCreate($search, ?callable $callback = null, array $options = []) + * @method DeTemplatedAccount patchEntity(EntityInterface $entity, array $data, array $options = []) + * @method array patchEntities(iterable $entities, array $data, array $options = []) + * @method DeTemplatedAccount|false save(EntityInterface $entity, array $options = []) + * @method DeTemplatedAccount saveOrFail(EntityInterface $entity, array $options = []) + * @method iterable|ResultSetInterface|false saveMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface saveManyOrFail(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface|false deleteMany(iterable $entities, array $options = []) + * @method iterable|ResultSetInterface deleteManyOrFail(iterable $entities, array $options = []) + * + * @mixin TreeBehavior + */ +class DeTemplatedAccountsTable extends Table +{ + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + + $this->setTable('de_templated_accounts'); + $this->setDisplayField('name'); + $this->setPrimaryKey('id'); + + $this->addBehavior('Tree'); + + $this->belongsTo('ParentDeTemplatedAccounts', [ + 'foreignKey' => 'parent_id', + 'className' => 'CakeAccounting.DeTemplatedAccounts', + ]); + $this->belongsTo('DeAccountTypes', [ + 'foreignKey' => 'account_type_code', + 'bindingKey' => 'account_type_code', + 'className' => 'CakeAccounting.DeAccountTypes', + ]); + $this->belongsTo('DeAccountTemplates', [ + 'foreignKey' => 'account_template_id', + 'className' => 'CakeAccounting.DeAccountTemplates', + ]); + $this->hasMany('ChildDeTemplatedAccounts', [ + 'foreignKey' => 'parent_id', + 'className' => 'CakeAccounting.DeTemplatedAccounts', + ]); + } + + /** + * Default validation rules. + * + * @param Validator $validator Validator instance. + * @return Validator + */ + public function validationDefault(Validator $validator): Validator + { + $validator + ->integer('account_number') + ->requirePresence('account_number', 'create') + ->notEmptyString('account_number'); + + $validator + ->integer('account_template_id') + ->requirePresence('account_template_id', 'create') + ->notEmptyString('account_template_id'); + + $validator + ->integer('parent_id') + ->allowEmptyString('parent_id'); + + $validator + ->scalar('account_type_code') + ->maxLength('account_type_code', 2) + ->requirePresence('account_type_code', 'create') + ->notEmptyString('account_type_code'); + + $validator + ->integer('account_limit') + ->allowEmptyString('account_limit'); + + $validator + ->boolean('can_credit') + ->notEmptyString('can_credit'); + + $validator + ->boolean('can_debit') + ->notEmptyString('can_debit'); + + $validator + ->scalar('name') + ->maxLength('name', 255) + ->requirePresence('name', 'create') + ->notEmptyString('name'); + + return $validator; + } + + /** + * Returns a rules checker object that will be used for validating + * application integrity. + * + * @param RulesChecker $rules The rules object to be modified. + * @return RulesChecker + */ + public function buildRules(RulesChecker $rules): RulesChecker + { + $rules->add($rules->existsIn(['parent_id'], 'ParentDeTemplatedAccounts'), ['errorField' => '0']); + + return $rules; + } +} diff --git a/src/Model/Table/DeTransactionTypesTable.php b/src/Model/Table/DeTransactionTypesTable.php new file mode 100644 index 0000000..21e92fa --- /dev/null +++ b/src/Model/Table/DeTransactionTypesTable.php @@ -0,0 +1,61 @@ + newEntities(array $data, array $options = []) + * @method \CakeAccounting\Model\Entity\DeTransactionType get(mixed $primaryKey, array|string $finder = 'all', \Psr\SimpleCache\CacheInterface|string|null $cache = null, \Closure|string|null $cacheKey = null, mixed ...$args) + * @method \CakeAccounting\Model\Entity\DeTransactionType findOrCreate($search, ?callable $callback = null, array $options = []) + * @method \CakeAccounting\Model\Entity\DeTransactionType patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = []) + * @method array<\CakeAccounting\Model\Entity\DeTransactionType> patchEntities(iterable $entities, array $data, array $options = []) + * @method \CakeAccounting\Model\Entity\DeTransactionType|false save(\Cake\Datasource\EntityInterface $entity, array $options = []) + * @method \CakeAccounting\Model\Entity\DeTransactionType saveOrFail(\Cake\Datasource\EntityInterface $entity, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeTransactionType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeTransactionType>|false saveMany(iterable $entities, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeTransactionType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeTransactionType> saveManyOrFail(iterable $entities, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeTransactionType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeTransactionType>|false deleteMany(iterable $entities, array $options = []) + * @method iterable<\CakeAccounting\Model\Entity\DeTransactionType>|\Cake\Datasource\ResultSetInterface<\CakeAccounting\Model\Entity\DeTransactionType> deleteManyOrFail(iterable $entities, array $options = []) + */ +class DeTransactionTypesTable extends Table +{ + /** + * Initialize method + * + * @param array $config The configuration for the Table. + * @return void + */ + public function initialize(array $config): void + { + parent::initialize($config); + + $this->setTable('de_transaction_types'); + $this->setDisplayField('name'); + $this->setPrimaryKey('transaction_type_code'); + } + + /** + * Default validation rules. + * + * @param \Cake\Validation\Validator $validator Validator instance. + * @return \Cake\Validation\Validator + */ + public function validationDefault(Validator $validator): Validator + { + $validator + ->scalar('name') + ->maxLength('name', 45) + ->requirePresence('name', 'create') + ->notEmptyString('name'); + + return $validator; + } +} diff --git a/src/Service/AccountingService.php b/src/Service/AccountingService.php new file mode 100644 index 0000000..50150d9 --- /dev/null +++ b/src/Service/AccountingService.php @@ -0,0 +1,257 @@ +DeAccounts = TableRegistry::getTableLocator()->get('CakeAccounting.DeAccounts'); + } + + public function getLastAccountStatement(DeAccount $deAccount) + { + $statementDate = date('Y') . '-' . date('m') . '-01'; + $accountStatement = $this->DeAccounts->DeAccountStatements->find() + ->where(['statement_date' => $statementDate, 'account_number' => $deAccount->account_number]) + ->first(); + if ($accountStatement) { + return $accountStatement; + } + $accountStatement = $this->DeAccounts->DeAccountStatements->find() + ->where(['account_number' => $deAccount->account_number]) + ->orderBy(['statement_date' => 'DESC']) + ->first(); + if (!$accountStatement) { + $newBlankStatement = $this->DeAccounts->DeAccountStatements->createFirstAccountSatement($deAccount); + + return $this->getLastAccountStatement($deAccount); + } + } + + public function getAccountBalance(DeAccount $deAccount) + { + $now = DateTime::now(); + Log::debug(print_r('inside get account balance de account number is next', true)); + Log::debug(print_r($deAccount->account_number, true)); + $lastAccountStatement = $this->getLastAccountStatement($deAccount); + +// Log::debug(print_r('$lastAccountStatement', true)); +// Log::debug(print_r("$lastAccountStatement", true)); + if (!$deAccount->children) { + Log::debug(print_r('no children &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&', true)); + + $debitSumsQuery = $this->DeAccounts->DebitDeJournalItems->find(); + $debitSumsQuery->select([ + 'debit_sum' => $debitSumsQuery->func()->sum('amount'), + 'account_number_debit', + ]) + ->where([ + 'account_number_debit' => $deAccount->account_number, + 'created >=' => $lastAccountStatement->statement_date, + ]); + + $debitSums = $debitSumsQuery->first(); + + $creditSumsQuery = $this->DeAccounts->DebitDeJournalItems->find(); + $creditSumsQuery->select([ + 'credit_sum' => $creditSumsQuery->func()->sum('amount'), + 'account_number_credit', + ]) + ->where([ + 'account_number_credit' => $deAccount->account_number, + 'created >=' => $lastAccountStatement->statement_date, + ]); + + $creditSums = $creditSumsQuery->first(); + +// Log::debug(print_r('$debitSums', true)); +// Log::debug(print_r($debitSums, true)); +// +// Log::debug(print_r('$creditSums', true)); +// Log::debug(print_r($creditSums, true)); + + $debitSum = $debitSums->debit_sum ?? 0; + $creditSum = $creditSums->credit_sum ?? 0; + + $lastAccountStatementBalance = isset($lastAccountStatement) ? $lastAccountStatement->closing_balance : 0; + + return [ + 'last_statement_balance' => $lastAccountStatementBalance, + 'total_credits_since' => $creditSum, + 'total_debits_since' => $debitSum, + 'balance' => bcadd(bcadd("$lastAccountStatementBalance", "$debitSum", 5), "$creditSum", 5), + 'children' => [], + ]; + } + $children = []; + $sumCredits = '0'; + $sumDebits = '0'; + $balance = '0'; + foreach ($deAccount->children as $child) { + $tmpAccountBalanceChildResult = $this->getAccountBalance($child); + $children[$child->account_number] = $tmpAccountBalanceChildResult; + + $childBalance = $this->getSumOfChildrenBalance($child, $tmpAccountBalanceChildResult); + $childDebits = $this->getSumOfChildrenBalance($child, $tmpAccountBalanceChildResult); + $childCredits = $this->getSumOfChildrenBalance($child, $tmpAccountBalanceChildResult); + $balance = bcadd("$balance", "$childBalance", 5); + $sumCredits = bcadd("$balance", "$childCredits", 5); + $sumDebits = bcadd("$balance", "$childDebits", 5); + } + + $lastAccountStatementBalance = isset($lastAccountStatement) ? $lastAccountStatement->closing_balance : 0; + + return [ + 'last_statement_balance' => $lastAccountStatementBalance, + 'total_credits_since' => $sumCredits, + 'total_debits_since' => $sumDebits, + 'balance' => bcadd("$lastAccountStatementBalance", "$balance", 5), + 'children' => $children, + ]; + } + + public function createAccountStatement(DeAccount $deAccount, Date $statementDate = null) + { + $debitsSumQuery = $this->DeAccounts->DebitDeJournalItems->find(); + $debitsSumQuery + ->select([ + 'debits_sum' => $debitsSumQuery->func()->sum('amount'), + ]) + ->find('debitsForAccountNumber', accountNumber: $deAccount->account_number) + ->all(); + + Log::debug(print_r('$debitsSumQuery', true)); + Log::debug(print_r($debitsSumQuery, true)); +// ->find('beforeDate', before: $statementDate) +// ->find('afterDate', before: $submittedAt) + $data = [ + 'account_number' => $deAccount->account_number, + 'statement_date' => $statementDate, + 'closing_balance' => 0, + ]; + $deAccountStatement = $this->DeAccounts->DeAccountStatements->newEntity($data); + if ($deAccountStatement->getErrors()) { + Log::debug(print_r('$deAccountStatement->getErrors() creating a new statement', true)); + Log::debug(print_r($deAccountStatement->getErrors(), true)); + } + + return $this->DeAccounts->DeAccountStatements->save($deAccountStatement); + } + + /** + * @param DeAccount $deAccount + * @param array $totals + * + * @return int|mixed|string + */ + public function getSumOfChildrenBalance(DeAccount $deAccount, array $totals = []) + { + Log::debug(print_r('inside accounting helper getSumOfChildrenBalance account number next', true)); + Log::debug(print_r($deAccount->account_number, true)); + Log::debug(print_r('$totals', true)); + Log::debug(print_r($totals, true)); + if (!isset($deAccount->children) || !$deAccount->children) { + Log::debug('no children exists in accounting helper returning next'); + + return array_key_exists('balance', $totals) ? $totals['balance'] : 0; + } + $balance = 0; + foreach ($deAccount->children as $child) { + Log::debug('inside foreach children loop getSumOfChildrenBalance'); + Log::debug('$child->account_number'); + Log::debug("$child->account_number"); + + $childTotals = array_key_exists('children', $totals) && array_key_exists($child->account_number, $totals['children']) ? $totals['children'][$child->account_number] : []; + + $childBalance = $this->getSumOfChildrenBalance($child, $childTotals ?? []); + $balance = bcadd("$balance", "$childBalance", 5); + } + + return $balance; + } + + /** + * @param DeAccount $deAccount + * @param array $totals + * + * @return int|mixed|string + */ + public function getSumOfChildrenDebits(DeAccount $deAccount, array $totals = []) + { +// Log::debug(print_r('inside accounting helper getSumOfChildrenDebits account number next', true)); +// Log::debug(print_r($deAccount->account_number, true)); +// Log::debug(print_r('$totals', true)); +// Log::debug(print_r($totals, true)); + if (!isset($deAccount->children) || !$deAccount->children) { + Log::debug('no children exists in accounting helper returning next'); + + return array_key_exists('total_debits_since', $totals) ? $totals['total_debits_since'] : 0; + } + $debits = 0; + foreach ($deAccount->children as $child) { +// Log::debug('inside foreach children loop getSumOfChildrenDebits'); +// Log::debug('$child->account_number'); +// Log::debug("$child->account_number"); + + $childTotals = array_key_exists('children', $totals) && array_key_exists($child->account_number, $totals['children']) ? $totals['children'][$child->account_number] : []; + + $childDebits = $this->getSumOfChildrenDebits($child, $childTotals ?? []); + $debits = bcadd("$debits", "$childDebits", 5); + } + + return $debits; + } + + /** + * @param DeAccount $deAccount + * @param array $totals + * + * @return int|mixed|string + */ + public function getSumOfChildrenCredits(DeAccount $deAccount, array $totals = []) + { +// Log::debug(print_r('inside accounting helper getSumOfChildrenCredits account number next', true)); +// Log::debug(print_r($deAccount->account_number, true)); +// Log::debug(print_r('$totals', true)); +// Log::debug(print_r($totals, true)); + if (!isset($deAccount->children) || !$deAccount->children) { + Log::debug('no children exists in accounting helper returning next'); + + return array_key_exists('total_credits_since', $totals) ? $totals['total_credits_since'] : 0; + } + $credits = 0; + foreach ($deAccount->children as $child) { +// Log::debug('inside foreach children loop getSumOfChildrenCredits'); +// Log::debug('$child->account_number'); +// Log::debug("$child->account_number"); + + $childTotals = array_key_exists('children', $totals) && array_key_exists($child->account_number, $totals['children']) ? $totals['children'][$child->account_number] : []; + + $childCredits = $this->getSumOfChildrenCredits($child, $childTotals ?? []); + $credits = bcadd("$credits", "$childCredits", 5); + } + + return $credits; + } +} diff --git a/src/View/Helper/AccountingHelper.php b/src/View/Helper/AccountingHelper.php new file mode 100644 index 0000000..7a772ac --- /dev/null +++ b/src/View/Helper/AccountingHelper.php @@ -0,0 +1,31 @@ + + */ + protected array $_defaultConfig = []; + + protected array $helpers = [ + 'Number', + ]; + + public function currency($number) + { + return $this->Number->currency($number, null, ['precision' => 5, 'places' => 5]); + } +} diff --git a/templates/DeAccountStatements/add.php b/templates/DeAccountStatements/add.php new file mode 100644 index 0000000..176b20a --- /dev/null +++ b/templates/DeAccountStatements/add.php @@ -0,0 +1,28 @@ + +
+ +
+
+ Form->create($deAccountStatement) ?> +
+ + Form->control('closing_balance'); + ?> +
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeAccountStatements/edit.php b/templates/DeAccountStatements/edit.php new file mode 100644 index 0000000..1b0c799 --- /dev/null +++ b/templates/DeAccountStatements/edit.php @@ -0,0 +1,33 @@ + +
+ +
+
+ Form->create($deAccountStatement) ?> +
+ + Form->control('closing_balance'); + ?> +
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeAccountStatements/index.php b/templates/DeAccountStatements/index.php new file mode 100644 index 0000000..be9db8e --- /dev/null +++ b/templates/DeAccountStatements/index.php @@ -0,0 +1,46 @@ + $deAccountStatements + */ +?> +
+ Html->link(__('New De Account Statement'), ['action' => 'add'], ['class' => 'button float-right']) ?> +

+
+ + + + + + + + + + + + + + + + + + + +
Paginator->sort('account_number') ?>Paginator->sort('statement_date') ?>Paginator->sort('closing_balance') ?>
hasValue('de_account') ? $this->Html->link($deAccountStatement->de_account->name, ['controller' => 'DeAccounts', 'action' => 'view', $deAccountStatement->de_account->id]) : '' ?>statement_date) ?>Number->format($deAccountStatement->closing_balance) ?> + Html->link(__('View'), ['action' => 'view', $deAccountStatement->account_number]) ?> + Html->link(__('Edit'), ['action' => 'edit', $deAccountStatement->account_number]) ?> + Form->postLink(__('Delete'), ['action' => 'delete', $deAccountStatement->account_number], ['confirm' => __('Are you sure you want to delete # {0}?', $deAccountStatement->account_number)]) ?> +
+
+
+
    + Paginator->first('<< ' . __('first')) ?> + Paginator->prev('< ' . __('previous')) ?> + Paginator->numbers() ?> + Paginator->next(__('next') . ' >') ?> + Paginator->last(__('last') . ' >>') ?> +
+

Paginator->counter(__('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')) ?>

+
+
diff --git a/templates/DeAccountStatements/view.php b/templates/DeAccountStatements/view.php new file mode 100644 index 0000000..12adab7 --- /dev/null +++ b/templates/DeAccountStatements/view.php @@ -0,0 +1,36 @@ + +
+ +
+
+

Array) ?>

+ + + + + + + + + + + + + +
hasValue('de_account') ? $this->Html->link($deAccountStatement->de_account->name, ['controller' => 'DeAccounts', 'action' => 'view', $deAccountStatement->de_account->id]) : '' ?>
Number->format($deAccountStatement->closing_balance) ?>
statement_date) ?>
+
+
+
diff --git a/templates/DeAccountTemplates/add.php b/templates/DeAccountTemplates/add.php new file mode 100644 index 0000000..25fd061 --- /dev/null +++ b/templates/DeAccountTemplates/add.php @@ -0,0 +1,28 @@ + +
+ +
+
+ Form->create($deAccountTemplate) ?> +
+ + Form->control('name'); + echo $this->Form->control('deleted', ['empty' => true]); + ?> +
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeAccountTemplates/edit.php b/templates/DeAccountTemplates/edit.php new file mode 100644 index 0000000..acf80a1 --- /dev/null +++ b/templates/DeAccountTemplates/edit.php @@ -0,0 +1,33 @@ + +
+ +
+
+ Form->create($deAccountTemplate) ?> +
+ + Form->control('name'); + echo $this->Form->control('deleted', ['empty' => true]); + ?> +
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeAccountTemplates/index.php b/templates/DeAccountTemplates/index.php new file mode 100644 index 0000000..6a963aa --- /dev/null +++ b/templates/DeAccountTemplates/index.php @@ -0,0 +1,52 @@ + $deAccountTemplates + */ +?> +
+ Html->link(__('New De Account Template'), ['action' => 'add'], ['class' => 'button float-right']) ?> +

+
+ + + + + + + + + + + + + + + + + + + +
Paginator->sort('id') ?>Paginator->sort('name') ?>Paginator->sort('deleted') ?>
Number->format($deAccountTemplate->id) ?>name) ?>deleted) ?> + Html->link(__('View'), ['action' => 'view', $deAccountTemplate->id]) ?> + Html->link(__('Edit'), ['action' => 'edit', $deAccountTemplate->id]) ?> + Form->postLink(__('Import'), [ + 'action' => 'select', + $deAccountTemplate->id, + ], [ + 'confirm' => __('Are you sure you want to import Accounts from Accounts Template "{0}"?', $deAccountTemplate->name), + ]); ?> + Form->postLink(__('Delete'), ['action' => 'delete', $deAccountTemplate->id], ['confirm' => __('Are you sure you want to delete # {0}?', $deAccountTemplate->id)]) ?> +
+
+
+
    + Paginator->first('<< ' . __('first')) ?> + Paginator->prev('< ' . __('previous')) ?> + Paginator->numbers() ?> + Paginator->next(__('next') . ' >') ?> + Paginator->last(__('last') . ' >>') ?> +
+

Paginator->counter(__('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')) ?>

+
+
diff --git a/templates/DeAccountTemplates/view.php b/templates/DeAccountTemplates/view.php new file mode 100644 index 0000000..bc16813 --- /dev/null +++ b/templates/DeAccountTemplates/view.php @@ -0,0 +1,79 @@ + +
+ +
+
+

name) ?>

+ + + + + + + + + + + + + +
name) ?>
Number->format($deAccountTemplate->id) ?>
deleted) ?>
+ +
+
+
diff --git a/templates/DeAccountTransactions/add.php b/templates/DeAccountTransactions/add.php new file mode 100644 index 0000000..3b38361 --- /dev/null +++ b/templates/DeAccountTransactions/add.php @@ -0,0 +1,30 @@ + +
+ +
+
+ Form->create($deAccountTransaction) ?> +
+ + Form->control('transaction_type_code'); + echo $this->Form->control('de_code'); + echo $this->Form->control('account_number'); + ?> +
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeAccountTransactions/index.php b/templates/DeAccountTransactions/index.php new file mode 100644 index 0000000..4f33ff8 --- /dev/null +++ b/templates/DeAccountTransactions/index.php @@ -0,0 +1,50 @@ + $deAccountTransactions + */ +?> +
+ Html->link(__('New De Account Transaction'), ['action' => 'add'], ['class' => 'button float-right']) ?> +

+
+ + + + + + + + + + + + + + + + + + + + + + + +
Paginator->sort('ledger_id') ?>Paginator->sort('submitted_at') ?>Paginator->sort('transaction_type_code') ?>Paginator->sort('de_code') ?>Paginator->sort('account_number') ?>
hasValue('de_transaction') ? $this->Html->link($deAccountTransaction->de_transaction->ledger_transaction_type_code, ['controller' => 'DeTransactions', 'action' => 'view', $deAccountTransaction->de_transaction->ledger_id]) : '' ?>submitted_at) ?>transaction_type_code) ?>de_code) ?>account_number) ?> + Html->link(__('View'), ['action' => 'view', $deAccountTransaction->ledger_id]) ?> + Html->link(__('Edit'), ['action' => 'edit', $deAccountTransaction->ledger_id]) ?> + Form->postLink(__('Delete'), ['action' => 'delete', $deAccountTransaction->ledger_id], ['confirm' => __('Are you sure you want to delete # {0}?', $deAccountTransaction->ledger_id)]) ?> +
+
+
+
    + Paginator->first('<< ' . __('first')) ?> + Paginator->prev('< ' . __('previous')) ?> + Paginator->numbers() ?> + Paginator->next(__('next') . ' >') ?> + Paginator->last(__('last') . ' >>') ?> +
+

Paginator->counter(__('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')) ?>

+
+
diff --git a/templates/DeAccountTransactions/view.php b/templates/DeAccountTransactions/view.php new file mode 100644 index 0000000..673b2d6 --- /dev/null +++ b/templates/DeAccountTransactions/view.php @@ -0,0 +1,44 @@ + +
+ +
+
+

transaction_type_code) ?>

+ + + + + + + + + + + + + + + + + + + + + +
hasValue('de_transaction') ? $this->Html->link($deAccountTransaction->de_transaction->ledger_transaction_type_code, ['controller' => 'DeTransactions', 'action' => 'view', $deAccountTransaction->de_transaction->ledger_id]) : '' ?>
transaction_type_code) ?>
de_code) ?>
account_number) ?>
submitted_at) ?>
+
+
+
diff --git a/templates/DeAccounts/add.php b/templates/DeAccounts/add.php new file mode 100644 index 0000000..63c2cee --- /dev/null +++ b/templates/DeAccounts/add.php @@ -0,0 +1,33 @@ + +
+ +
+
+ Form->create($deAccount) ?> +
+ + Form->control('parent_id', ['options' => $accounts, 'empty' => true]); + echo $this->Form->control('account_type_code', ['options' => $deAccountTypes]); + echo $this->Form->control('account_number'); + echo $this->Form->control('name'); + echo $this->Form->control('account_limit'); + ?> +
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeAccounts/edit.php b/templates/DeAccounts/edit.php new file mode 100644 index 0000000..47c6e4d --- /dev/null +++ b/templates/DeAccounts/edit.php @@ -0,0 +1,40 @@ + +
+ +
+
+ Form->create($deAccount) ?> +
+ + Form->control('account_number'); + echo $this->Form->control('parent_id', ['options' => $parentDeAccounts, 'empty' => true]); + echo $this->Form->control('account_type_code', ['options' => $deAccountTypes]); + echo $this->Form->control('name'); + echo $this->Form->control('account_limit'); + echo $this->Form->control('can_credit'); + echo $this->Form->control('can_debit'); + ?> +
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeAccounts/index.php b/templates/DeAccounts/index.php new file mode 100644 index 0000000..7df99a9 --- /dev/null +++ b/templates/DeAccounts/index.php @@ -0,0 +1,36 @@ + $deAccounts + */ +?> +
+ Html->link(__('Use Account Templates'), [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountTemplates', + 'action' => 'index' + ], ['class' => 'button float-right']) ?> + Html->link(__('New De Account'), ['action' => 'add'], ['class' => 'button float-right']) ?> +

+
+ + + + + + + + + + + + + element('DeAccounts/row', [ + 'account' => $deAccount, + 'totals' => array_key_exists($deAccount->account_number, $totals) ? $totals[$deAccount->account_number] : [], + ]); ?> + + +
+
+
diff --git a/templates/DeAccounts/view.php b/templates/DeAccounts/view.php new file mode 100644 index 0000000..f7b5902 --- /dev/null +++ b/templates/DeAccounts/view.php @@ -0,0 +1,131 @@ + +
+ +
+
+

name) ?>

+
+ account_number, $totals) && array_key_exists('balance', $totals[$deAccount->account_number]) ? $this->Number->format($totals[$deAccount->account_number]['balance']) : 0; ?> + +
+ + hasValue('parent_de_account')) : ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ hasValue('parent_de_account') ? $this->Html->link($deAccount->parent_de_account->name, [ + 'controller' => 'DeAccounts', + 'action' => 'view', + $deAccount->parent_de_account->id, + ]) : '' ?>
hasValue('de_account_type') ? $this->Html->link($deAccount->de_account_type->name, ['controller' => 'DeAccountTypes', 'action' => 'view', $deAccount->de_account_type->account_type_code]) : '' ?>
name) ?>
account_number ?>
account_limit === null ? '' : $this->Number->format($deAccount->account_limit) ?>
can_credit ? __('Yes') : __('No'); ?>
can_debit ? __('Yes') : __('No'); ?>
+ + +
+
+
diff --git a/templates/DeExternalAccountStatements/add.php b/templates/DeExternalAccountStatements/add.php new file mode 100644 index 0000000..550fe66 --- /dev/null +++ b/templates/DeExternalAccountStatements/add.php @@ -0,0 +1,29 @@ + +
+ +
+
+ Form->create($deExternalAccountStatement) ?> +
+ + Form->control('closing_balance'); + echo $this->Form->control('total_credit'); + echo $this->Form->control('total_debit'); + ?> +
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeExternalAccountStatements/edit.php b/templates/DeExternalAccountStatements/edit.php new file mode 100644 index 0000000..028233d --- /dev/null +++ b/templates/DeExternalAccountStatements/edit.php @@ -0,0 +1,34 @@ + +
+ +
+
+ Form->create($deExternalAccountStatement) ?> +
+ + Form->control('closing_balance'); + echo $this->Form->control('total_credit'); + echo $this->Form->control('total_debit'); + ?> +
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeExternalAccountStatements/index.php b/templates/DeExternalAccountStatements/index.php new file mode 100644 index 0000000..0640a38 --- /dev/null +++ b/templates/DeExternalAccountStatements/index.php @@ -0,0 +1,50 @@ + $deExternalAccountStatements + */ +?> +
+ Html->link(__('New De External Account Statement'), ['action' => 'add'], ['class' => 'button float-right']) ?> +

+
+ + + + + + + + + + + + + + + + + + + + + + + +
Paginator->sort('external_account_number') ?>Paginator->sort('statement_date') ?>Paginator->sort('closing_balance') ?>Paginator->sort('total_credit') ?>Paginator->sort('total_debit') ?>
external_account_number) ?>statement_date) ?>Number->format($deExternalAccountStatement->closing_balance) ?>Number->format($deExternalAccountStatement->total_credit) ?>Number->format($deExternalAccountStatement->total_debit) ?> + Html->link(__('View'), ['action' => 'view', $deExternalAccountStatement->external_account_number]) ?> + Html->link(__('Edit'), ['action' => 'edit', $deExternalAccountStatement->external_account_number]) ?> + Form->postLink(__('Delete'), ['action' => 'delete', $deExternalAccountStatement->external_account_number], ['confirm' => __('Are you sure you want to delete # {0}?', $deExternalAccountStatement->external_account_number)]) ?> +
+
+
+
    + Paginator->first('<< ' . __('first')) ?> + Paginator->prev('< ' . __('previous')) ?> + Paginator->numbers() ?> + Paginator->next(__('next') . ' >') ?> + Paginator->last(__('last') . ' >>') ?> +
+

Paginator->counter(__('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')) ?>

+
+
diff --git a/templates/DeExternalAccountStatements/view.php b/templates/DeExternalAccountStatements/view.php new file mode 100644 index 0000000..5e94b05 --- /dev/null +++ b/templates/DeExternalAccountStatements/view.php @@ -0,0 +1,44 @@ + +
+ +
+
+

Array) ?>

+ + + + + + + + + + + + + + + + + + + + + +
external_account_number) ?>
Number->format($deExternalAccountStatement->closing_balance) ?>
Number->format($deExternalAccountStatement->total_credit) ?>
Number->format($deExternalAccountStatement->total_debit) ?>
statement_date) ?>
+
+
+
diff --git a/templates/DeExternalAccounts/add.php b/templates/DeExternalAccounts/add.php new file mode 100644 index 0000000..f394b7b --- /dev/null +++ b/templates/DeExternalAccounts/add.php @@ -0,0 +1,29 @@ + +
+ +
+
+ Form->create($deExternalAccount) ?> +
+ + Form->control('entity_type_code'); + echo $this->Form->control('related_model'); + echo $this->Form->control('related_model_fk'); + ?> +
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeExternalAccounts/edit.php b/templates/DeExternalAccounts/edit.php new file mode 100644 index 0000000..445441b --- /dev/null +++ b/templates/DeExternalAccounts/edit.php @@ -0,0 +1,34 @@ + +
+ +
+
+ Form->create($deExternalAccount) ?> +
+ + Form->control('entity_type_code'); + echo $this->Form->control('related_model'); + echo $this->Form->control('related_model_fk'); + ?> +
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeExternalAccounts/index.php b/templates/DeExternalAccounts/index.php new file mode 100644 index 0000000..4fa743c --- /dev/null +++ b/templates/DeExternalAccounts/index.php @@ -0,0 +1,50 @@ + $deExternalAccounts + */ +?> +
+ Html->link(__('New De External Account'), ['action' => 'add'], ['class' => 'button float-right']) ?> +

+
+ + + + + + + + + + + + + + + + + + + + + + + +
Paginator->sort('external_account_number') ?>Paginator->sort('entity_type_code') ?>Paginator->sort('created') ?>Paginator->sort('related_model') ?>Paginator->sort('related_model_fk') ?>
external_account_number) ?>entity_type_code) ?>created) ?>related_model) ?>related_model_fk === null ? '' : $this->Number->format($deExternalAccount->related_model_fk) ?> + Html->link(__('View'), ['action' => 'view', $deExternalAccount->external_account_number]) ?> + Html->link(__('Edit'), ['action' => 'edit', $deExternalAccount->external_account_number]) ?> + Form->postLink(__('Delete'), ['action' => 'delete', $deExternalAccount->external_account_number], ['confirm' => __('Are you sure you want to delete # {0}?', $deExternalAccount->external_account_number)]) ?> +
+
+
+
    + Paginator->first('<< ' . __('first')) ?> + Paginator->prev('< ' . __('previous')) ?> + Paginator->numbers() ?> + Paginator->next(__('next') . ' >') ?> + Paginator->last(__('last') . ' >>') ?> +
+

Paginator->counter(__('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')) ?>

+
+
diff --git a/templates/DeExternalAccounts/view.php b/templates/DeExternalAccounts/view.php new file mode 100644 index 0000000..8a09a33 --- /dev/null +++ b/templates/DeExternalAccounts/view.php @@ -0,0 +1,44 @@ + +
+ +
+
+

entity_type_code) ?>

+ + + + + + + + + + + + + + + + + + + + + +
external_account_number) ?>
entity_type_code) ?>
related_model) ?>
related_model_fk === null ? '' : $this->Number->format($deExternalAccount->related_model_fk) ?>
created) ?>
+
+
+
diff --git a/templates/DeJournalEntries/add.php b/templates/DeJournalEntries/add.php new file mode 100644 index 0000000..1d4d765 --- /dev/null +++ b/templates/DeJournalEntries/add.php @@ -0,0 +1,84 @@ + +
+ +
+
+ Form->create($deJournalEntry) ?> + + Form->control('de_journal_id', ['options' => $deJournals]); + + ?> +
+
+ + Form->control('de_journal_items.0.account_number_debit', [ + 'type' => 'select', + 'options' => $accounts, + 'label' => 'Account', + 'empty' => true, + 'required' => true, + ]); + echo $this->Form->control('de_journal_items.0.external_account_number_debit', [ + 'type' => 'select', + 'options' => $externalAccounts ?? [], + 'label' => 'External Account', + 'empty' => true, + 'required' => false, + ]); + ?> +
+
+ + Form->control('de_journal_items.0.amount', [ + 'label' => false, + ]); + ?> +
+
+ + + Form->control('de_journal_items.0.account_number_credit', [ + 'type' => 'select', + 'options' => $accounts, + 'label' => 'Account', + 'empty' => true, + 'required' => true, + ]); + echo $this->Form->control('de_journal_items.0.external_account_number_credit', [ + 'type' => 'select', + 'options' => $externalAccounts ?? [], + 'label' => 'External Account', + 'empty' => true, + 'required' => false, + ]); + ?> +
+
+
+
+ Form->control('notes', ['type' => 'textarea', 'rows' => 2]); + ?> +
+
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeJournalEntries/edit.php b/templates/DeJournalEntries/edit.php new file mode 100644 index 0000000..26f481f --- /dev/null +++ b/templates/DeJournalEntries/edit.php @@ -0,0 +1,35 @@ + +
+ +
+
+ Form->create($deJournalEntry) ?> +
+ + Form->control('de_journal_id', ['options' => $deJournals]); + echo $this->Form->control('notes'); + ?> +
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeJournalEntries/index.php b/templates/DeJournalEntries/index.php new file mode 100644 index 0000000..fc53f3e --- /dev/null +++ b/templates/DeJournalEntries/index.php @@ -0,0 +1,48 @@ + $deJournalEntries + */ +?> +
+ Html->link(__('New De Journal Entry'), ['action' => 'add'], ['class' => 'button float-right']) ?> +

+
+ + + + + + + + + + + + + + + + + + + + + +
Paginator->sort('id') ?>Paginator->sort('de_journal_id') ?>Paginator->sort('user_id') ?>Paginator->sort('created') ?>
Number->format($deJournalEntry->id) ?>hasValue('de_journal') ? $this->Html->link($deJournalEntry->de_journal->name, ['controller' => 'DeJournals', 'action' => 'view', $deJournalEntry->de_journal->id]) : '' ?>hasValue('user') ? $this->Html->link($deJournalEntry->user->first_name, ['controller' => 'Users', 'action' => 'view', $deJournalEntry->user->id]) : '' ?>created) ?> + Html->link(__('View'), ['action' => 'view', $deJournalEntry->id]) ?> + Html->link(__('Edit'), ['action' => 'edit', $deJournalEntry->id]) ?> + Form->postLink(__('Delete'), ['action' => 'delete', $deJournalEntry->id], ['confirm' => __('Are you sure you want to delete # {0}?', $deJournalEntry->id)]) ?> +
+
+
+
    + Paginator->first('<< ' . __('first')) ?> + Paginator->prev('< ' . __('previous')) ?> + Paginator->numbers() ?> + Paginator->next(__('next') . ' >') ?> + Paginator->last(__('last') . ' >>') ?> +
+

Paginator->counter(__('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')) ?>

+
+
diff --git a/templates/DeJournalEntries/view.php b/templates/DeJournalEntries/view.php new file mode 100644 index 0000000..8284215 --- /dev/null +++ b/templates/DeJournalEntries/view.php @@ -0,0 +1,83 @@ + +
+ +
+
+

id) ?>

+ + + + + + + + + + + + + + + + + +
hasValue('de_journal') ? $this->Html->link($deJournalEntry->de_journal->name, ['controller' => 'DeJournals', 'action' => 'view', $deJournalEntry->de_journal->id]) : '' ?>
hasValue('user') ? $this->Html->link($deJournalEntry->user->first_name, ['controller' => 'Users', 'action' => 'view', $deJournalEntry->user->id]) : '' ?>
Number->format($deJournalEntry->id) ?>
created) ?>
+
+ +
+ Text->autoParagraph(h($deJournalEntry->notes)); ?> +
+
+ +
+
+
diff --git a/templates/DeJournalItems/add.php b/templates/DeJournalItems/add.php new file mode 100644 index 0000000..3ac879b --- /dev/null +++ b/templates/DeJournalItems/add.php @@ -0,0 +1,33 @@ + +
+ +
+
+ Form->create($deJournalItem) ?> +
+ + Form->control('account_number_credit'); + echo $this->Form->control('account_number_debit'); + echo $this->Form->control('external_account_number_credit'); + echo $this->Form->control('external_account_number_debit'); + echo $this->Form->control('de_journal_entry_id', ['options' => $deJournalEntries]); + echo $this->Form->control('amount'); + ?> +
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeJournalItems/edit.php b/templates/DeJournalItems/edit.php new file mode 100644 index 0000000..5e7f2b3 --- /dev/null +++ b/templates/DeJournalItems/edit.php @@ -0,0 +1,38 @@ + +
+ +
+
+ Form->create($deJournalItem) ?> +
+ + Form->control('account_number_credit'); + echo $this->Form->control('account_number_debit'); + echo $this->Form->control('external_account_number_credit'); + echo $this->Form->control('external_account_number_debit'); + echo $this->Form->control('de_journal_entry_id', ['options' => $deJournalEntries]); + echo $this->Form->control('amount'); + ?> +
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeJournalItems/index.php b/templates/DeJournalItems/index.php new file mode 100644 index 0000000..00f5efd --- /dev/null +++ b/templates/DeJournalItems/index.php @@ -0,0 +1,123 @@ + $deJournalItems + */ +?> +
+ Html->link(__('New De Journal Entry'), ['controller' => 'DeJournalEntries', 'action' => 'add'], ['class' => 'button float-right']) ?> +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Paginator->sort('created') ?>Paginator->sort('journal_entry.journal_id', 'Journal') ?>Paginator->sort('de_journal_entry_id', 'Journal Entry') ?>Paginator->sort('external_account_number_credit', 'DR Ext. Account') ?>Paginator->sort('external_account_number_debit', 'CR Ext. Account') ?>Paginator->sort('account_number_credit', 'Credit Account') ?>Paginator->sort('account_number_debit', 'Debit Account') ?>Paginator->sort('account_number_debit', 'Debit') ?>Paginator->sort('account_number_credit', 'Credit') ?>
created) ?> + hasValue('de_journal_entry') && $deJournalItem->de_journal_entry->hasValue('de_journal') ? + $this->Html->link($deJournalItem->de_journal_entry->de_journal->name, ['controller' => 'DeJournals', 'action' => 'view', $deJournalItem->de_journal_entry->journal_id]) : + '' ?> + + hasValue('de_journal_entry') ? + $this->Html->link($deJournalItem->de_journal_entry->id, ['controller' => 'DeJournalEntries', 'action' => 'view', $deJournalItem->de_journal_entry->id]) : + '' ?> + + hasValue('debit_de_external_account') ? + $this->Html->link($deJournalItem->debit_de_external_account->account_number, [ + 'controller' => 'DeExternalAccounts', + 'action' => 'view', + $deJournalItem->debit_de_external_account->account_number]) : + '' ?> + + hasValue('debit_de_account') ? + $this->Html->link($deJournalItem->debit_de_account->account_number_and_name, [ + 'controller' => 'DeAccounts', + 'action' => 'view', + $deJournalItem->debit_de_account->account_number]) : + '' ?> + hasValue('debit_de_account') ? $this->Number->format($deJournalItem->amount) : ''; ?> + Html->link(__('View'), ['action' => 'view', $deJournalItem->id]) ?> + Html->link(__('Edit'), ['action' => 'edit', $deJournalItem->id]) ?> + Form->postLink(__('Delete'), ['action' => 'delete', $deJournalItem->id], ['confirm' => __('Are you sure you want to delete # {0}?', $deJournalItem->id)]) ?> +
created) ?> + hasValue('de_journal_entry') && $deJournalItem->de_journal_entry->hasValue('de_journal') ? + $this->Html->link($deJournalItem->de_journal_entry->de_journal->name, ['controller' => 'DeJournals', 'action' => 'view', $deJournalItem->de_journal_entry->journal_id]) : + '' ?> + + hasValue('de_journal_entry') ? + $this->Html->link($deJournalItem->de_journal_entry->id, ['controller' => 'DeJournalEntries', 'action' => 'view', $deJournalItem->de_journal_entry->id]) : + '' ?> + + hasValue('credit_de_external_account') ? + $this->Html->link($deJournalItem->credit_de_external_account->account_number, [ + 'controller' => 'DeExternalAccounts', + 'action' => 'view', + $deJournalItem->credit_de_external_account->account_number]) : + '' ?> + + hasValue('credit_de_account') ? + $this->Html->link($deJournalItem->credit_de_account->account_number_and_name, [ + 'controller' => 'DeAccounts', + 'action' => 'view', + $deJournalItem->credit_de_account->account_number]) : + '' ?> + hasValue('credit_de_account') ? $this->Number->format($deJournalItem->amount) : ''; ?> + Html->link(__('View'), ['action' => 'view', $deJournalItem->id]) ?> + Html->link(__('Edit'), ['action' => 'edit', $deJournalItem->id]) ?> + Form->postLink(__('Delete'), ['action' => 'delete', $deJournalItem->id], ['confirm' => __('Are you sure you want to delete # {0}?', $deJournalItem->id)]) ?> +
+
+
+
    + Paginator->first('<< ' . __('first')) ?> + Paginator->prev('< ' . __('previous')) ?> + Paginator->numbers() ?> + Paginator->next(__('next') . ' >') ?> + Paginator->last(__('last') . ' >>') ?> +
+

Paginator->counter(__('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')) ?>

+
+
diff --git a/templates/DeJournalItems/view.php b/templates/DeJournalItems/view.php new file mode 100644 index 0000000..c5e5f24 --- /dev/null +++ b/templates/DeJournalItems/view.php @@ -0,0 +1,56 @@ + +
+ +
+
+

id) ?>

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
hasValue('de_journal_entry') ? $this->Html->link($deJournalItem->de_journal_entry->id, ['controller' => 'DeJournalEntries', 'action' => 'view', $deJournalItem->de_journal_entry->id]) : '' ?>
Number->format($deJournalItem->id) ?>
Number->format($deJournalItem->account_number_credit) ?>
Number->format($deJournalItem->account_number_debit) ?>
external_account_number_credit === null ? '' : $this->Number->format($deJournalItem->external_account_number_credit) ?>
external_account_number_debit === null ? '' : $this->Number->format($deJournalItem->external_account_number_debit) ?>
Number->format($deJournalItem->amount) ?>
created) ?>
+
+
+
diff --git a/templates/DeJournals/add.php b/templates/DeJournals/add.php new file mode 100644 index 0000000..9898c79 --- /dev/null +++ b/templates/DeJournals/add.php @@ -0,0 +1,31 @@ + +
+ +
+
+ Form->create($deJournal) ?> +
+ + Form->control('name'); + echo $this->Form->control('short_code'); + echo $this->Form->control('default_account_number_credit'); + echo $this->Form->control('default_account_number_debit'); + echo $this->Form->control('de_journal_type_code'); + ?> +
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeJournals/edit.php b/templates/DeJournals/edit.php new file mode 100644 index 0000000..5e335be --- /dev/null +++ b/templates/DeJournals/edit.php @@ -0,0 +1,36 @@ + +
+ +
+
+ Form->create($deJournal) ?> +
+ + Form->control('name'); + echo $this->Form->control('short_code'); + echo $this->Form->control('default_account_number_credit'); + echo $this->Form->control('default_account_number_debit'); + echo $this->Form->control('de_journal_type_code'); + ?> +
+ Form->button(__('Submit')) ?> + Form->end() ?> +
+
+
diff --git a/templates/DeJournals/index.php b/templates/DeJournals/index.php new file mode 100644 index 0000000..62a80e6 --- /dev/null +++ b/templates/DeJournals/index.php @@ -0,0 +1,52 @@ + $deJournals + */ +?> +
+ Html->link(__('New De Journal'), ['action' => 'add'], ['class' => 'button float-right']) ?> +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Paginator->sort('id') ?>Paginator->sort('name') ?>Paginator->sort('short_code') ?>Paginator->sort('default_account_number_credit') ?>Paginator->sort('default_account_number_debit') ?>Paginator->sort('de_journal_type_code') ?>
Number->format($deJournal->id) ?>name) ?>short_code) ?>default_account_number_credit === null ? '' : $this->Number->format($deJournal->default_account_number_credit) ?>default_account_number_debit === null ? '' : $this->Number->format($deJournal->default_account_number_debit) ?>de_journal_type_code) ?> + Html->link(__('View'), ['action' => 'view', $deJournal->id]) ?> + Html->link(__('Edit'), ['action' => 'edit', $deJournal->id]) ?> + Form->postLink(__('Delete'), ['action' => 'delete', $deJournal->id], ['confirm' => __('Are you sure you want to delete # {0}?', $deJournal->id)]) ?> +
+
+
+
    + Paginator->first('<< ' . __('first')) ?> + Paginator->prev('< ' . __('previous')) ?> + Paginator->numbers() ?> + Paginator->next(__('next') . ' >') ?> + Paginator->last(__('last') . ' >>') ?> +
+

Paginator->counter(__('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')) ?>

+
+
diff --git a/templates/DeJournals/view.php b/templates/DeJournals/view.php new file mode 100644 index 0000000..09e80d5 --- /dev/null +++ b/templates/DeJournals/view.php @@ -0,0 +1,79 @@ + +
+ +
+
+

name) ?>

+ + + + + + + + + + + + + + + + + + + + + + + + + +
name) ?>
short_code) ?>
de_journal_type_code) ?>
Number->format($deJournal->id) ?>
default_account_number_credit === null ? '' : $this->Number->format($deJournal->default_account_number_credit) ?>
default_account_number_debit === null ? '' : $this->Number->format($deJournal->default_account_number_debit) ?>
+ +
+
+
diff --git a/templates/element/DeAccounts/row.php b/templates/element/DeAccounts/row.php new file mode 100644 index 0000000..5ab2f14 --- /dev/null +++ b/templates/element/DeAccounts/row.php @@ -0,0 +1,38 @@ +children) && $account->children; +?> + + account_type_code ?> + account_number ?> + name) ?> + + Number->format($totals['balance']) : 0; ?> + + + Html->link(__('View'), ['action' => 'view', $account->account_number]) ?> + + + + children as $child) : ?> + element('DeAccounts/row', [ + 'account' => $child, + 'nestLevel' => $nestLevel, + 'totals' => array_key_exists($child->account_number, $totals['children']) ? $totals['children'][$child->account_number] : [], + ]); ?> + + diff --git a/templates/element/Layout/submenu.php b/templates/element/Layout/submenu.php new file mode 100644 index 0000000..f8126a1 --- /dev/null +++ b/templates/element/Layout/submenu.php @@ -0,0 +1,44 @@ +ActiveLink->link('Accounts', [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccounts', + 'action' => 'index', +],[ + 'class' => 'submenu-link', + 'target' => [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccounts', + ], +]); ?> +ActiveLink->link('External Accounts', [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccounts', + 'action' => 'index', +], [ + 'class' => 'submenu-link', + 'target' => [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccounts', + ], +]); ?> +ActiveLink->link('Journal Entries', [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournalEntries', + 'action' => 'index', +], [ + 'class' => 'submenu-link', + 'target' => [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournalEntries', + ], +]); ?> +ActiveLink->link('JournalItems', [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournalItems', + 'action' => 'index', +], [ + 'class' => 'submenu-link', + 'target' => [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournalItems', + ], +]); ?> diff --git a/tests/Fixture/DeAccountStatementsFixture.php b/tests/Fixture/DeAccountStatementsFixture.php new file mode 100644 index 0000000..874ad2c --- /dev/null +++ b/tests/Fixture/DeAccountStatementsFixture.php @@ -0,0 +1,30 @@ +records = [ + [ + 'account_statement_id' => '4614401e-fe0c-45cf-9f17-c45c7848960e', + 'account_number' => 1, + 'statement_date' => '2024-11-02', + 'closing_balance' => 1.5, + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeAccountTemplatesFixture.php b/tests/Fixture/DeAccountTemplatesFixture.php new file mode 100644 index 0000000..cef58a4 --- /dev/null +++ b/tests/Fixture/DeAccountTemplatesFixture.php @@ -0,0 +1,29 @@ +records = [ + [ + 'id' => 1, + 'name' => 'Lorem ipsum dolor sit amet', + 'deleted' => '2024-11-02 04:09:57', + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeAccountTypesFixture.php b/tests/Fixture/DeAccountTypesFixture.php new file mode 100644 index 0000000..8419d63 --- /dev/null +++ b/tests/Fixture/DeAccountTypesFixture.php @@ -0,0 +1,52 @@ +records = [ + [ + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Asset', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, + 'name' => 'Liability', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_EQUITY, + 'name' => 'Equity', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_GAIN, + 'name' => 'Gain', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_LOSS, + 'name' => 'Loss', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Expenses', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_REVENUE, + 'name' => 'Revenue', + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeAccountsFixture.php b/tests/Fixture/DeAccountsFixture.php new file mode 100644 index 0000000..4ec838a --- /dev/null +++ b/tests/Fixture/DeAccountsFixture.php @@ -0,0 +1,348 @@ +records = [ + [ + 'id' => 1, + 'account_number' => 10000, + 'parent_id' => null, + 'lft' => 1, + 'rght' => 14, + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Assets', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 2, + 'account_number' => 11000, + 'parent_id' => 1, + 'lft' => 2, + 'rght' => 3, + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Cash', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 3, + 'account_number' => 12000, + 'parent_id' => 1, + 'lft' => 4, + 'rght' => 5, + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Investments', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 4, + 'account_number' => 13000, + 'parent_id' => 1, + 'lft' => 6, + 'rght' => 7, + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Accounts Receivable', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 5, + 'account_number' => 14000, + 'parent_id' => 1, + 'lft' => 8, + 'rght' => 9, + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Inventory', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 6, + 'account_number' => 15000, + 'parent_id' => 1, + 'lft' => 10, + 'rght' => 11, + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Prepaid Expenses', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 7, + 'account_number' => 16000, + 'parent_id' => 1, + 'lft' => 12, + 'rght' => 13, + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Property & Equipment', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 8, + 'account_number' => 20000, + 'parent_id' => null, + 'lft' => 15, + 'rght' => 26, + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, + 'name' => 'Liabilities', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 9, + 'account_number' => 21000, + 'parent_id' => 8, + 'lft' => 16, + 'rght' => 17, + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, + 'name' => 'Accounts Payable', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 10, + 'account_number' => 22000, + 'parent_id' => 8, + 'lft' => 18, + 'rght' => 19, + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, + 'name' => 'Accrued Payroll Expenses', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 11, + 'account_number' => 23000, + 'parent_id' => 8, + 'lft' => 20, + 'rght' => 21, + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, + 'name' => 'Accrued Expenses', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 12, + 'account_number' => 24000, + 'parent_id' => 8, + 'lft' => 22, + 'rght' => 23, + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, + 'name' => 'Taxes', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 13, + 'account_number' => 25000, + 'parent_id' => 8, + 'lft' => 24, + 'rght' => 25, + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, + 'name' => 'Long Term Debt', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 14, + 'account_number' => 30000, + 'parent_id' => null, + 'lft' => 27, + 'rght' => 32, + 'account_type_code' => DE_ACCOUNT_TYPE_EQUITY, + 'name' => 'Equity', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 15, + 'account_number' => 31000, + 'parent_id' => 14, + 'lft' => 28, + 'rght' => 29, + 'account_type_code' => DE_ACCOUNT_TYPE_EQUITY, + 'name' => 'Owner\'s Equity', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 16, + 'account_number' => 32000, + 'parent_id' => 14, + 'lft' => 30, + 'rght' => 31, + 'account_type_code' => DE_ACCOUNT_TYPE_EQUITY, + 'name' => 'Retained Earnings', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 17, + 'account_number' => 40000, + 'parent_id' => null, + 'lft' => 33, + 'rght' => 40, + 'account_type_code' => DE_ACCOUNT_TYPE_REVENUE, + 'name' => 'Revenue', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 18, + 'account_number' => 41000, + 'parent_id' => 17, + 'lft' => 34, + 'rght' => 39, + 'account_type_code' => DE_ACCOUNT_TYPE_REVENUE, + 'name' => 'Sales', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 19, + 'account_number' => 41100, + 'parent_id' => 18, + 'lft' => 35, + 'rght' => 36, + 'account_type_code' => DE_ACCOUNT_TYPE_REVENUE, + 'name' => 'Product Sales', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 20, + 'account_number' => 41200, + 'parent_id' => 18, + 'lft' => 37, + 'rght' => 38, + 'account_type_code' => DE_ACCOUNT_TYPE_REVENUE, + 'name' => 'Service Sales', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 21, + 'account_number' => 50000, + 'parent_id' => null, + 'lft' => 41, + 'rght' => 54, + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Expenses', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 22, + 'account_number' => 51000, + 'parent_id' => 21, + 'lft' => 42, + 'rght' => 43, + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Payroll Expenses', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 23, + 'account_number' => 52000, + 'parent_id' => 21, + 'lft' => 44, + 'rght' => 45, + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Rent & Utilities', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 24, + 'account_number' => 53000, + 'parent_id' => 21, + 'lft' => 46, + 'rght' => 47, + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Marketing', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 25, + 'account_number' => 54000, + 'parent_id' => 21, + 'lft' => 48, + 'rght' => 49, + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Insurance', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 26, + 'account_number' => 55000, + 'parent_id' => 21, + 'lft' => 50, + 'rght' => 51, + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Cost of Goods Sold', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 27, + 'account_number' => 56000, + 'parent_id' => 21, + 'lft' => 52, + 'rght' => 53, + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Other Expenses', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeBankAccountTypesFixture.php b/tests/Fixture/DeBankAccountTypesFixture.php new file mode 100644 index 0000000..6821bd0 --- /dev/null +++ b/tests/Fixture/DeBankAccountTypesFixture.php @@ -0,0 +1,44 @@ +records = [ + [ + 'bank_account_type_code' => DE_BANK_ACCOUNT_TYPE_CHECKING, + 'de_code' => 'Cr', + 'name' => 'Checking', + ], + [ + 'bank_account_type_code' => DE_BANK_ACCOUNT_TYPE_LINE_OF_CREDIT, + 'de_code' => 'Dr', + 'name' => 'Line of Credit', + ], + [ + 'bank_account_type_code' => DE_BANK_ACCOUNT_TYPE_MORTGAGE, + 'de_code' => 'Dr', + 'name' => 'Mortgage', + ], + [ + 'bank_account_type_code' => DE_BANK_ACCOUNT_TYPE_SAVINGS, + 'de_code' => 'Cr', + 'name' => 'Savings', + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeBaseTypesFixture.php b/tests/Fixture/DeBaseTypesFixture.php new file mode 100644 index 0000000..451d138 --- /dev/null +++ b/tests/Fixture/DeBaseTypesFixture.php @@ -0,0 +1,32 @@ +records = [ + [ + 'de_code' => DE_TYPE_CREDIT, + 'name' => 'Credit', + ], + [ + 'de_code' => DE_TYPE_DEBIT, + 'name' => 'Debit', + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeEntityTypesFixture.php b/tests/Fixture/DeEntityTypesFixture.php new file mode 100644 index 0000000..cda7cda --- /dev/null +++ b/tests/Fixture/DeEntityTypesFixture.php @@ -0,0 +1,40 @@ +records = [ + [ + 'entity_type_code' => DE_ENTITY_TYPE_BANK_ACCOUNT, + 'name' => 'Bank Account', + ], + [ + 'entity_type_code' => DE_ENTITY_TYPE_PERSON, + 'name' => 'Person', + ], + [ + 'entity_type_code' => DE_ENTITY_TYPE_ORGANIZATION, + 'name' => 'Organization', + ], + [ + 'entity_type_code' => DE_ENTITY_TYPE_PROPERTY, + 'name' => 'Property', + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeExternalAccountStatementsFixture.php b/tests/Fixture/DeExternalAccountStatementsFixture.php new file mode 100644 index 0000000..04c8c8a --- /dev/null +++ b/tests/Fixture/DeExternalAccountStatementsFixture.php @@ -0,0 +1,32 @@ +records = [ + [ + 'external_account_statement_id' => '2f83162b-2bec-4826-8853-fbda2fac3a95', + 'external_account_number' => 'ded3dbc1-bfb3-4e7f-b4d0-a6af01724441', + 'statement_date' => '2024-11-02', + 'closing_balance' => 1.5, + 'total_credit' => 1.5, + 'total_debit' => 1.5, + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeExternalAccountsFixture.php b/tests/Fixture/DeExternalAccountsFixture.php new file mode 100644 index 0000000..0d1e173 --- /dev/null +++ b/tests/Fixture/DeExternalAccountsFixture.php @@ -0,0 +1,31 @@ +records = [ + [ + 'external_account_number' => '2463dc4f-6f38-4492-8c8e-918f11e13748', + 'entity_type_code' => DE_ENTITY_TYPE_ORGANIZATION, + 'created' => '2024-11-02 02:25:23', + 'related_model' => 'Clients', + 'related_model_fk' => 1, + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeInternalAccountTypesFixture.php b/tests/Fixture/DeInternalAccountTypesFixture.php new file mode 100644 index 0000000..bf0e464 --- /dev/null +++ b/tests/Fixture/DeInternalAccountTypesFixture.php @@ -0,0 +1,52 @@ +records = [ + [ + 'account_type_code' => DE_ACCOUNT_TYPE_ASSET, + 'name' => 'Asset', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, + 'name' => 'Liability', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_EQUITY, + 'name' => 'Equity', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_GAIN, + 'name' => 'Gain', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_LOSS, + 'name' => 'Loss', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_EXPENSES, + 'name' => 'Expenses', + ], + [ + 'account_type_code' => DE_ACCOUNT_TYPE_REVENUE, + 'name' => 'Revenue', + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeJournalEntriesFixture.php b/tests/Fixture/DeJournalEntriesFixture.php new file mode 100644 index 0000000..7218ff4 --- /dev/null +++ b/tests/Fixture/DeJournalEntriesFixture.php @@ -0,0 +1,31 @@ +records = [ + [ + 'id' => 1, + 'de_journal_id' => 2, + 'user_id' => 1, + 'created' => '2024-11-01 07:55:15', + 'notes' => 'Lorem ipsumttis convallis.', + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeJournalItemsFixture.php b/tests/Fixture/DeJournalItemsFixture.php new file mode 100644 index 0000000..eecdf69 --- /dev/null +++ b/tests/Fixture/DeJournalItemsFixture.php @@ -0,0 +1,34 @@ +records = [ + [ + 'id' => 1, + 'account_number_credit' => 11000, // assets->cash + 'account_number_debit' => 21000, // accounts payable + 'external_account_number_credit' => null, + 'external_account_number_debit' => '2463dc4f-6f38-4492-8c8e-918f11e13748', + 'de_journal_entry_id' => 1, + 'amount' => 500, + 'created' => '2024-11-01 07:54:55', + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeJournalTypesFixture.php b/tests/Fixture/DeJournalTypesFixture.php new file mode 100644 index 0000000..96371af --- /dev/null +++ b/tests/Fixture/DeJournalTypesFixture.php @@ -0,0 +1,39 @@ +records = [ + [ + 'de_journal_type_code' => 'BANK', + ], + [ + 'de_journal_type_code' => 'CASH', + ], + [ + 'de_journal_type_code' => 'MISC', + ], + [ + 'de_journal_type_code' => 'PURCH', + ], + [ + 'de_journal_type_code' => 'SALES', + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeJournalsFixture.php b/tests/Fixture/DeJournalsFixture.php new file mode 100644 index 0000000..36f164b --- /dev/null +++ b/tests/Fixture/DeJournalsFixture.php @@ -0,0 +1,64 @@ +records = [ + [ + 'id' => 1, + 'name' => 'Customer Invoices', + 'short_code' => 'SALES', + 'default_account_number_credit' => null, + 'default_account_number_debit' => null, + 'de_journal_type_code' => DE_JOURNAL_TYPE_SALES, + ], + [ + 'id' => 2, + 'name' => 'Vendor Bills', + 'short_code' => 'BILLS', + 'default_account_number_credit' => null, + 'default_account_number_debit' => null, + 'de_journal_type_code' => DE_JOURNAL_TYPE_PURCHASING, + ], + [ + 'id' => 3, + 'name' => 'Bank', + 'short_code' => 'BANK', + 'default_account_number_credit' => null, + 'default_account_number_debit' => null, + 'de_journal_type_code' => DE_JOURNAL_TYPE_BANK, + ], + [ + 'id' => 4, + 'name' => 'Misc.', + 'short_code' => 'MISC', + 'default_account_number_credit' => null, + 'default_account_number_debit' => null, + 'de_journal_type_code' => DE_JOURNAL_TYPE_MISC, + ], + [ + 'id' => 5, + 'name' => 'Cash', + 'short_code' => 'CASH', + 'default_account_number_credit' => null, + 'default_account_number_debit' => null, + 'de_journal_type_code' => DE_JOURNAL_TYPE_CASH, + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeLedgerStatementsFixture.php b/tests/Fixture/DeLedgerStatementsFixture.php new file mode 100644 index 0000000..ab342f8 --- /dev/null +++ b/tests/Fixture/DeLedgerStatementsFixture.php @@ -0,0 +1,29 @@ +records = [ + [ + 'account_number' => 11000, + 'statement_date' => '2024-11-01', + 'closing_balance' => 0, + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeLedgerTemplatesFixture.php b/tests/Fixture/DeLedgerTemplatesFixture.php new file mode 100644 index 0000000..5c4655b --- /dev/null +++ b/tests/Fixture/DeLedgerTemplatesFixture.php @@ -0,0 +1,29 @@ +records = [ + [ + 'id' => 1, + 'name' => 'Basic', + 'deleted' => null, + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeLedgersFixture.php b/tests/Fixture/DeLedgersFixture.php new file mode 100644 index 0000000..dd6fb9e --- /dev/null +++ b/tests/Fixture/DeLedgersFixture.php @@ -0,0 +1,348 @@ +records = [ + [ + 'id' => 1, + 'account_number' => 10000, + 'parent_id' => null, + 'lft' => 1, + 'rght' => 14, + 'account_type_code' => 'AA', + 'name' => 'Assets', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 2, + 'account_number' => 11000, + 'parent_id' => 1, + 'lft' => 2, + 'rght' => 3, + 'account_type_code' => 'AA', + 'name' => 'Cash', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 3, + 'account_number' => 12000, + 'parent_id' => 1, + 'lft' => 4, + 'rght' => 5, + 'account_type_code' => 'AA', + 'name' => 'Investments', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 4, + 'account_number' => 13000, + 'parent_id' => 1, + 'lft' => 6, + 'rght' => 7, + 'account_type_code' => 'AA', + 'name' => 'Accounts Receivable', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 5, + 'account_number' => 14000, + 'parent_id' => 1, + 'lft' => 8, + 'rght' => 9, + 'account_type_code' => 'AA', + 'name' => 'Inventory', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 6, + 'account_number' => 15000, + 'parent_id' => 1, + 'lft' => 10, + 'rght' => 11, + 'account_type_code' => 'AA', + 'name' => 'Prepaid Expenses', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 7, + 'account_number' => 16000, + 'parent_id' => 1, + 'lft' => 12, + 'rght' => 13, + 'account_type_code' => 'AA', + 'name' => 'Property & Equipment', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 8, + 'account_number' => 20000, + 'parent_id' => null, + 'lft' => 15, + 'rght' => 26, + 'account_type_code' => 'AL', + 'name' => 'Liabilities', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 9, + 'account_number' => 21000, + 'parent_id' => 8, + 'lft' => 16, + 'rght' => 17, + 'account_type_code' => 'AL', + 'name' => 'Accounts Payable', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 10, + 'account_number' => 22000, + 'parent_id' => 8, + 'lft' => 18, + 'rght' => 19, + 'account_type_code' => 'AL', + 'name' => 'Accrued Payroll Expenses', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 11, + 'account_number' => 23000, + 'parent_id' => 8, + 'lft' => 20, + 'rght' => 21, + 'account_type_code' => 'AL', + 'name' => 'Accrued Expenses', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 12, + 'account_number' => 24000, + 'parent_id' => 8, + 'lft' => 22, + 'rght' => 23, + 'account_type_code' => 'AL', + 'name' => 'Taxes', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 13, + 'account_number' => 25000, + 'parent_id' => 8, + 'lft' => 24, + 'rght' => 25, + 'account_type_code' => 'AL', + 'name' => 'Long Term Debt', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 14, + 'account_number' => 30000, + 'parent_id' => null, + 'lft' => 27, + 'rght' => 32, + 'account_type_code' => 'EQ', + 'name' => 'Equity', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 15, + 'account_number' => 31000, + 'parent_id' => 14, + 'lft' => 28, + 'rght' => 29, + 'account_type_code' => 'EQ', + 'name' => 'Owner\'s Equity', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 16, + 'account_number' => 32000, + 'parent_id' => 14, + 'lft' => 30, + 'rght' => 31, + 'account_type_code' => 'EQ', + 'name' => 'Retained Earnings', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 17, + 'account_number' => 40000, + 'parent_id' => null, + 'lft' => 33, + 'rght' => 40, + 'account_type_code' => 'RR', + 'name' => 'Revenue', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 18, + 'account_number' => 41000, + 'parent_id' => 17, + 'lft' => 34, + 'rght' => 39, + 'account_type_code' => 'RR', + 'name' => 'Sales', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 19, + 'account_number' => 41100, + 'parent_id' => 18, + 'lft' => 35, + 'rght' => 36, + 'account_type_code' => 'RR', + 'name' => 'Product Sales', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 20, + 'account_number' => 41200, + 'parent_id' => 18, + 'lft' => 37, + 'rght' => 38, + 'account_type_code' => 'RR', + 'name' => 'Service Sales', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 21, + 'account_number' => 50000, + 'parent_id' => null, + 'lft' => 41, + 'rght' => 54, + 'account_type_code' => 'RE', + 'name' => 'Expenses', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 22, + 'account_number' => 51000, + 'parent_id' => 21, + 'lft' => 42, + 'rght' => 43, + 'account_type_code' => 'RE', + 'name' => 'Payroll Expenses', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 23, + 'account_number' => 52000, + 'parent_id' => 21, + 'lft' => 44, + 'rght' => 45, + 'account_type_code' => 'RE', + 'name' => 'Rent & Utilities', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 24, + 'account_number' => 53000, + 'parent_id' => 21, + 'lft' => 46, + 'rght' => 47, + 'account_type_code' => 'RE', + 'name' => 'Marketing', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 25, + 'account_number' => 54000, + 'parent_id' => 21, + 'lft' => 48, + 'rght' => 49, + 'account_type_code' => 'RE', + 'name' => 'Insurance', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 26, + 'account_number' => 55000, + 'parent_id' => 21, + 'lft' => 50, + 'rght' => 51, + 'account_type_code' => 'RE', + 'name' => 'Cost of Goods Sold', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + [ + 'id' => 27, + 'account_number' => 56000, + 'parent_id' => 21, + 'lft' => 52, + 'rght' => 53, + 'account_type_code' => 'RE', + 'name' => 'Other Expenses', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeTemplatedAccountsFixture.php b/tests/Fixture/DeTemplatedAccountsFixture.php new file mode 100644 index 0000000..528baf9 --- /dev/null +++ b/tests/Fixture/DeTemplatedAccountsFixture.php @@ -0,0 +1,37 @@ +records = [ + [ + 'id' => 1, + 'account_number' => 1, + 'account_template_id' => 1, + 'parent_id' => 1, + 'lft' => 1, + 'rght' => 1, + 'account_type_code' => 'Lo', + 'account_limit' => 1, + 'can_credit' => 1, + 'can_debit' => 1, + 'name' => 'Lorem ipsum dolor sit amet', + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeTemplatedLedgersFixture.php b/tests/Fixture/DeTemplatedLedgersFixture.php new file mode 100644 index 0000000..8bbefa6 --- /dev/null +++ b/tests/Fixture/DeTemplatedLedgersFixture.php @@ -0,0 +1,375 @@ +records = [ + [ + 'id' => 1, + 'account_number' => 10000, + 'account_template_id' => 1, + 'parent_id' => null, + 'lft' => 1, + 'rght' => 14, + 'account_type_code' => 'AA', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Assets', + ], + [ + 'id' => 2, + 'account_number' => 20000, + 'account_template_id' => 1, + 'parent_id' => null, + 'lft' => 15, + 'rght' => 26, + 'account_type_code' => 'AL', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Liabilities', + ], + [ + 'id' => 3, + 'account_number' => 30000, + 'account_template_id' => 1, + 'parent_id' => null, + 'lft' => 27, + 'rght' => 32, + 'account_type_code' => 'EQ', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Equity', + ], + [ + 'id' => 4, + 'account_number' => 40000, + 'account_template_id' => 1, + 'parent_id' => null, + 'lft' => 33, + 'rght' => 40, + 'account_type_code' => 'RR', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Revenue', + ], + [ + 'id' => 5, + 'account_number' => 50000, + 'account_template_id' => 1, + 'parent_id' => null, + 'lft' => 41, + 'rght' => 54, + 'account_type_code' => 'RE', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Expenses', + ], + [ + 'id' => 6, + 'account_number' => 11000, + 'account_template_id' => 1, + 'parent_id' => 1, + 'lft' => 2, + 'rght' => 3, + 'account_type_code' => 'AA', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Cash', + ], + [ + 'id' => 7, + 'account_number' => 12000, + 'account_template_id' => 1, + 'parent_id' => 1, + 'lft' => 4, + 'rght' => 5, + 'account_type_code' => 'AA', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Investments', + ], + [ + 'id' => 8, + 'account_number' => 13000, + 'account_template_id' => 1, + 'parent_id' => 1, + 'lft' => 6, + 'rght' => 7, + 'account_type_code' => 'AA', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Accounts Receivable', + ], + [ + 'id' => 9, + 'account_number' => 14000, + 'account_template_id' => 1, + 'parent_id' => 1, + 'lft' => 8, + 'rght' => 9, + 'account_type_code' => 'AA', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Inventory', + ], + [ + 'id' => 10, + 'account_number' => 15000, + 'account_template_id' => 1, + 'parent_id' => 1, + 'lft' => 10, + 'rght' => 11, + 'account_type_code' => 'AA', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Prepaid Expenses', + ], + [ + 'id' => 11, + 'account_number' => 16000, + 'account_template_id' => 1, + 'parent_id' => 1, + 'lft' => 12, + 'rght' => 13, + 'account_type_code' => 'AA', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Property & Equipment', + ], + [ + 'id' => 12, + 'account_number' => 21000, + 'account_template_id' => 1, + 'parent_id' => 2, + 'lft' => 16, + 'rght' => 17, + 'account_type_code' => 'AL', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Accounts Payable', + ], + [ + 'id' => 13, + 'account_number' => 22000, + 'account_template_id' => 1, + 'parent_id' => 2, + 'lft' => 18, + 'rght' => 19, + 'account_type_code' => 'AL', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Accrued Payroll Expenses', + ], + [ + 'id' => 14, + 'account_number' => 23000, + 'account_template_id' => 1, + 'parent_id' => 2, + 'lft' => 20, + 'rght' => 21, + 'account_type_code' => 'AL', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Accrued Expenses', + ], + [ + 'id' => 15, + 'account_number' => 24000, + 'account_template_id' => 1, + 'parent_id' => 2, + 'lft' => 22, + 'rght' => 23, + 'account_type_code' => 'AL', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Taxes', + ], + [ + 'id' => 16, + 'account_number' => 25000, + 'account_template_id' => 1, + 'parent_id' => 2, + 'lft' => 24, + 'rght' => 25, + 'account_type_code' => 'AL', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Long Term Debt', + ], + [ + 'id' => 17, + 'account_number' => 31000, + 'account_template_id' => 1, + 'parent_id' => 3, + 'lft' => 28, + 'rght' => 29, + 'account_type_code' => 'EQ', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Owner\'s Equity', + ], + [ + 'id' => 18, + 'account_number' => 32000, + 'account_template_id' => 1, + 'parent_id' => 3, + 'lft' => 30, + 'rght' => 31, + 'account_type_code' => 'EQ', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Retained Earnings', + ], + [ + 'id' => 19, + 'account_number' => 41000, + 'account_template_id' => 1, + 'parent_id' => 4, + 'lft' => 34, + 'rght' => 39, + 'account_type_code' => 'RR', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Sales', + ], + [ + 'id' => 20, + 'account_number' => 41100, + 'account_template_id' => 1, + 'parent_id' => 19, + 'lft' => 35, + 'rght' => 36, + 'account_type_code' => 'RR', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Product Sales', + ], + [ + 'id' => 21, + 'account_number' => 41200, + 'account_template_id' => 1, + 'parent_id' => 19, + 'lft' => 37, + 'rght' => 38, + 'account_type_code' => 'RR', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Service Sales', + ], + [ + 'id' => 22, + 'account_number' => 51000, + 'account_template_id' => 1, + 'parent_id' => 5, + 'lft' => 42, + 'rght' => 43, + 'account_type_code' => 'RE', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Payroll Expenses', + ], + [ + 'id' => 23, + 'account_number' => 52000, + 'account_template_id' => 1, + 'parent_id' => 5, + 'lft' => 44, + 'rght' => 45, + 'account_type_code' => 'RE', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Rent & Utilities', + ], + [ + 'id' => 24, + 'account_number' => 53000, + 'account_template_id' => 1, + 'parent_id' => 5, + 'lft' => 46, + 'rght' => 47, + 'account_type_code' => 'RE', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Marketing', + ], + [ + 'id' => 25, + 'account_number' => 54000, + 'account_template_id' => 1, + 'parent_id' => 5, + 'lft' => 48, + 'rght' => 49, + 'account_type_code' => 'RE', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Insurance', + ], + [ + 'id' => 26, + 'account_number' => 55000, + 'account_template_id' => 1, + 'parent_id' => 5, + 'lft' => 50, + 'rght' => 51, + 'account_type_code' => 'RE', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Cost of Goods Sold', + ], + [ + 'id' => 27, + 'account_number' => 56000, + 'account_template_id' => 1, + 'parent_id' => 5, + 'lft' => 52, + 'rght' => 53, + 'account_type_code' => 'RE', + 'account_limit' => null, + 'can_credit' => true, + 'can_debit' => true, + 'name' => 'Other Expenses', + ], + ]; + parent::init(); + } +} diff --git a/tests/Fixture/DeTransactionTypesFixture.php b/tests/Fixture/DeTransactionTypesFixture.php new file mode 100644 index 0000000..0fa8261 --- /dev/null +++ b/tests/Fixture/DeTransactionTypesFixture.php @@ -0,0 +1,48 @@ +records = [ + [ + 'transaction_type_code' => 'AC', + 'name' => 'Adjust Credit', + ], + [ + 'transaction_type_code' => 'AD', + 'name' => 'Adjust Debit', + ], + [ + 'transaction_type_code' => 'Dp', + 'name' => 'Deposit', + ], + [ + 'transaction_type_code' => 'EC', + 'name' => 'Estimate Credit', + ], + [ + 'transaction_type_code' => 'ED', + 'name' => 'Estimate Debit', + ], + [ + 'transaction_type_code' => 'Wd', + 'name' => 'Withdrawal', + ], + ]; + parent::init(); + } +} diff --git a/tests/TestCase/Controller/BaseControllerTest.php b/tests/TestCase/Controller/BaseControllerTest.php new file mode 100644 index 0000000..2b3a44a --- /dev/null +++ b/tests/TestCase/Controller/BaseControllerTest.php @@ -0,0 +1,25 @@ +session(['Auth.User.id' => 1]); + $this->session(['Auth.id' => 1]); + } + + /** + * @return void + */ + public function testTest() + { + $this->assertEquals(1, 1); + } +} diff --git a/tests/TestCase/Controller/Component/AccountingComponentTest.php b/tests/TestCase/Controller/Component/AccountingComponentTest.php new file mode 100644 index 0000000..8c4a74d --- /dev/null +++ b/tests/TestCase/Controller/Component/AccountingComponentTest.php @@ -0,0 +1,45 @@ +Accounting = new AccountingComponent($registry); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->Accounting); + + parent::tearDown(); + } +} diff --git a/tests/TestCase/Controller/DeAccountStatementsControllerTest.php b/tests/TestCase/Controller/DeAccountStatementsControllerTest.php new file mode 100644 index 0000000..17c420c --- /dev/null +++ b/tests/TestCase/Controller/DeAccountStatementsControllerTest.php @@ -0,0 +1,146 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeAccountStatements', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $this->DeAccountStatements = $this->getTableLocator()->get('DeAccountStatements'); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeAccountStatements); + + parent::tearDown(); + } + + /** + * Test index method + * + * Tests the index action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeAccountStatementsController::index + */ + public function testIndexGetUnauthenticated(): void + { + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountStatements', + 'action' => 'index', + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test index method + * + * Tests the index action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountStatementsController::index + */ + public function testIndexGetLoggedIn(): void + { + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountStatements', + 'action' => 'index', + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test view method + * + * Tests the view action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeAccountStatementsController::view + */ + public function testViewGetUnauthenticated(): void + { + $id = '4614401e-fe0c-45cf-9f17-c45c7848960e'; + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountStatements', + 'action' => 'view', + $id, + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test view method + * + * Tests the view action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountStatementsController::view + */ + public function testViewGetLoggedIn(): void + { + $id = '4614401e-fe0c-45cf-9f17-c45c7848960e'; + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountStatements', + 'action' => 'view', + $id, + ]; + $this->get($url); + $this->assertResponseCode(200); + } +} diff --git a/tests/TestCase/Controller/DeAccountTemplatesControllerTest.php b/tests/TestCase/Controller/DeAccountTemplatesControllerTest.php new file mode 100644 index 0000000..92a0a1c --- /dev/null +++ b/tests/TestCase/Controller/DeAccountTemplatesControllerTest.php @@ -0,0 +1,482 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeAccountTemplates', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $this->DeAccountTemplates = $this->getTableLocator()->get('DeAccountTemplates'); + $this->DeAccounts = $this->getTableLocator()->get('DeAccounts'); + $this->DeTemplatedAccounts = $this->getTableLocator()->get('DeTemplatedAccounts'); + $this->enableCsrfToken(); + $this->enableSecurityToken(); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeAccountTemplates); + + parent::tearDown(); + } + + /** + * Test index method + * + * Tests the index action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeAccountTemplatesController::index + */ + public function testIndexGetUnauthenticated(): void + { + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountTemplates', + 'action' => 'index', + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test index method + * + * Tests the index action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountTemplatesController::index + */ + public function testIndexGetLoggedIn(): void + { + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountTemplates', + 'action' => 'index', + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test view method + * + * Tests the view action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeAccountTemplatesController::view + */ + public function testViewGetUnauthenticated(): void + { + $id = 1; + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountTemplates', + 'action' => 'view', + $id, + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test view method + * + * Tests the view action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountTemplatesController::view + */ + public function testViewGetLoggedIn(): void + { + $id = 1; + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountTemplates', + 'action' => 'view', + $id, + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test select method + * + * Tests the select method which should import templated accounts as real accounts + * + * @throws Exception + * @return void + * + * @uses DeAccountTemplatesController::select + */ + public function testSelectPostUnauthenticated(): void + { + $cntBefore = $this->DeAccounts->find()->count(); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountTemplates', + 'action' => 'select', + 1, + ]; + $this->post($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + + $cntAfter = $this->DeAccounts->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test select method + * + * Tests the select method which should import templated accounts as real accounts + * + * @throws Exception + * @return void + * + * @uses DeAccountTemplatesController::select + */ + public function testSelectPostValid(): void + { + $beforeAccounts = $this->DeAccounts->find(); + foreach ($beforeAccounts as $beforeAccount) { + $this->DeAccounts->delete($beforeAccount); + } + + $beforeImporting = $this->DeAccounts->find()->count(); + $this->assertEquals(0, $beforeImporting); + + $templatedAccounts = $this->DeTemplatedAccounts->find()->where(['account_template_id' => 1])->count(); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountTemplates', + 'action' => 'select', + 1, + ]; + $this->loginUserByRole('admin'); + $this->post($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('de-accounts'); + + $after = $this->DeAccounts->find()->count(); + $this->assertEquals($templatedAccounts, $after); + } + + /** + * Test add method + * + * Tests the add action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountTemplatesController::add + */ + public function testAddGetLoggedIn(): void + { + $cntBefore = $this->DeAccountTemplates->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountTemplates', + 'action' => 'add', + ]; + $this->get($url); + $this->assertResponseCode(200); + + $cntAfter = $this->DeAccountTemplates->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test add method + * + * Tests a POST request to the add action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountTemplatesController::add + */ + public function testAddPostLoggedInSuccess(): void + { + $cntBefore = $this->DeAccountTemplates->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountTemplates', + 'action' => 'add', + ]; + $data = [ + 'name' => 'testing', + ]; + $this->post($url, $data); + $this->assertResponseCode(302); + $this->assertRedirectContains('de-account-templates'); + + $cntAfter = $this->DeAccountTemplates->find()->count(); + $this->assertEquals($cntBefore + 1, $cntAfter); + } + + /** + * Test add method + * + * Tests a POST request to the add action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountTemplatesController::add + */ + public function testAddPostLoggedInFailure(): void + { + $cntBefore = $this->DeAccountTemplates->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountTemplates', + 'action' => 'add', + ]; + $data = [ + 'name' => '', + ]; + $this->post($url, $data); + $this->assertResponseCode(200); + + $cntAfter = $this->DeAccountTemplates->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test edit method + * + * Tests the edit action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeAccountTemplatesController::edit + */ + public function testEditGetUnauthenticated(): void + { + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountTemplates', + 'action' => 'edit', + 1, + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test edit method + * + * Tests the edit action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountTemplatesController::edit + */ + public function testEditGetLoggedIn(): void + { + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountTemplates', + 'action' => 'edit', + 1, + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test edit method + * + * Tests a PUT request to the edit action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountTemplatesController::edit + */ + public function testEditPutLoggedInSuccess(): void + { + $this->loginUserByRole('admin'); + $id = 1; + $before = $this->DeAccountTemplates->get($id); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountTemplates', + 'action' => 'edit', + $id, + ]; + $data = [ + // test new data here + 'name' => 'updated name', + ]; + $this->put($url, $data); + + $this->assertResponseCode(302); + $this->assertRedirectContains('de-account-templates'); + + $after = $this->DeAccountTemplates->get($id); + $this->assertEquals($data['name'], $after->name); + // assert saved properly below + } + + /** + * Test edit method + * + * Tests a PUT request to the edit action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountTemplatesController::edit + */ + public function testEditPutLoggedInFailure(): void + { + $this->loginUserByRole('admin'); + $id = 1; + $before = $this->DeAccountTemplates->get($id); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountTemplates', + 'action' => 'edit', + $id, + ]; + $data = [ + 'name' => '', + ]; + $this->put($url, $data); + $this->assertResponseCode(200); + $after = $this->DeAccountTemplates->get($id); + + // assert save failed below + } + + /** + * Test delete method + * + * Tests the delete action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeAccountTemplatesController::delete + */ + public function testDeleteUnauthenticated(): void + { + $cntBefore = $this->DeAccountTemplates->find()->count(); + + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountTemplates', + 'action' => 'delete', + 1, + ]; + $this->delete($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + + $cntAfter = $this->DeAccountTemplates->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test delete method + * + * Tests the delete action with a logged in user + * + * @return void + *@throws Exception + * + * @uses DeAccountTemplatesController::delete + */ + public function testDeleteLoggedIn(): void + { + $cntBefore = $this->DeAccountTemplates->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccountTemplates', + 'action' => 'delete', + 1, + ]; + $this->delete($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('de-account-templates'); + + $cntAfter = $this->DeAccountTemplates->find()->count(); + $this->assertEquals($cntBefore - 1, $cntAfter); + } +} diff --git a/tests/TestCase/Controller/DeAccountsControllerTest.php b/tests/TestCase/Controller/DeAccountsControllerTest.php new file mode 100644 index 0000000..d4572dd --- /dev/null +++ b/tests/TestCase/Controller/DeAccountsControllerTest.php @@ -0,0 +1,447 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeAccounts', + 'plugin.CakeAccounting.DeAccountTypes', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $this->DeAccounts = $this->getTableLocator()->get('DeAccounts'); + $this->enableCsrfToken(); + $this->enableSecurityToken(); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeAccounts); + + parent::tearDown(); + } + + /** + * Test index method + * + * Tests the index action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeAccountsController::index + */ + public function testIndexGetUnauthenticated(): void + { + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccounts', + 'action' => 'index', + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test index method + * + * Tests the index action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountsController::index + */ + public function testIndexGetLoggedIn(): void + { + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccounts', + 'action' => 'index', + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test view method + * + * Tests the view action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeAccountsController::view + */ + public function testViewGetUnauthenticated(): void + { + $id = 41200; + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccounts', + 'action' => 'view', + $id, + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test view method + * + * Tests the view action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountsController::view + */ + public function testViewGetLoggedIn(): void + { + $id = 41200; + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccounts', + 'action' => 'view', + $id, + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test add method + * + * Tests the add action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeAccountsController::add + */ + public function testAddGetUnauthenticated(): void + { + $cntBefore = $this->DeAccounts->find()->count(); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccounts', + 'action' => 'add', + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + + $cntAfter = $this->DeAccounts->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test add method + * + * Tests the add action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountsController::add + */ + public function testAddGetLoggedIn(): void + { + $cntBefore = $this->DeAccounts->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccounts', + 'action' => 'add', + ]; + $this->get($url); + $this->assertResponseCode(200); + + $cntAfter = $this->DeAccounts->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test add method + * + * Tests a POST request to the add action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountsController::add + */ + public function testAddPostLoggedInSuccess(): void + { + $cntBefore = $this->DeAccounts->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccounts', + 'action' => 'add', + ]; + $data = [ + 'parent_id' => '12000', + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, // wrong account type for the parent - parent = assets + 'account_number' => '12900', + 'name' => 'Crypto Investments', + 'account_limit' => '', + ]; + $this->post($url, $data); + $this->assertResponseCode(302); + $this->assertRedirectContains('de-accounts'); + + $cntAfter = $this->DeAccounts->find()->count(); + $this->assertEquals($cntBefore + 1, $cntAfter); + + $justAdded = $this->DeAccounts->find()->where(['account_number' => '12900'])->firstOrFail(); + $this->assertEquals(DE_ACCOUNT_TYPE_ASSET, $justAdded->account_type_code); + } + + /** + * Test add method + * + * Tests a POST request to the add action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountsController::add + */ + public function testAddPostLoggedInFailure(): void + { + $cntBefore = $this->DeAccounts->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccounts', + 'action' => 'add', + ]; + $data = []; + $this->post($url, $data); + $this->assertResponseCode(200); + + $cntAfter = $this->DeAccounts->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test edit method + * + * Tests the edit action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeAccountsController::edit + */ + public function testEditGetUnauthenticated(): void + { + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccounts', + 'action' => 'edit', + 41200, + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test edit method + * + * Tests the edit action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountsController::edit + */ + public function testEditGetLoggedIn(): void + { + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccounts', + 'action' => 'edit', + 41200, + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test edit method + * + * Tests a PUT request to the edit action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountsController::edit + */ + public function testEditPutLoggedInSuccess(): void + { + $this->loginUserByRole('admin'); + $id = 41200; + $before = $this->DeAccounts->find()->where(['account_number' => $id])->firstOrFail(); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccounts', + 'action' => 'edit', + $id, + ]; + $data = [ + // test new data here + 'parent_id' => '41000', + 'account_type_code' => DE_ACCOUNT_TYPE_LIABILITY, // wrong account type for the parent - parent = revenue + 'account_number' => '41300', // changed from 41200 - no journal entries or children so should be allowed + 'name' => 'Sales - Services', + 'account_limit' => '', + ]; + $this->put($url, $data); + + $this->assertResponseCode(302); + $this->assertRedirectContains('de-accounts'); + + $after = $this->DeAccounts->find()->where(['account_number' => 41300])->firstOrFail(); + // assert saved properly below + $this->assertEquals(DE_ACCOUNT_TYPE_REVENUE, $after->account_type_code); + $this->assertEquals('Sales - Services', $after->name); + $this->assertEquals('41300', $after->account_number); + } + + /** + * Test edit method + * + * Tests a PUT request to the edit action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeAccountsController::edit + */ + public function testEditPutLoggedInFailure(): void + { + $this->loginUserByRole('admin'); + $id = 41200; + $before = $this->DeAccounts->find()->where(['account_number' => $id])->firstOrFail(); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccounts', + 'action' => 'edit', + $id, + ]; + $data = [ +// 'parent_id' => 9999999999999, + 'account_number' => '', + 'name' => 'should fail', + ]; + $this->put($url, $data); + $this->assertResponseCode(200); + $after = $this->DeAccounts->find()->where(['account_number' => $id])->firstOrFail(); + $this->assertEquals($before->account_number, $after->account_number); + $this->assertEquals($before->name, $after->name); + $this->assertEquals($before->parent_id, $after->parent_id); + // assert save failed below + } + + /** + * Test delete method + * + * Tests the delete action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeAccountsController::delete + */ + public function testDeleteUnauthenticated(): void + { + $cntBefore = $this->DeAccounts->find()->count(); + + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccounts', + 'action' => 'delete', + 41200, + ]; + $this->delete($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + + $cntAfter = $this->DeAccounts->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test delete method + * + * Tests the delete action with a logged in user + * + * @return void + *@throws Exception + * + * @uses DeAccountsController::delete + */ + public function testDeleteLoggedIn(): void + { + $cntBefore = $this->DeAccounts->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeAccounts', + 'action' => 'delete', + 41200, + ]; + $this->delete($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('de-accounts'); + + $cntAfter = $this->DeAccounts->find()->count(); + $this->assertEquals($cntBefore - 1, $cntAfter); + } +} diff --git a/tests/TestCase/Controller/DeExternalAccountStatementsControllerTest.php b/tests/TestCase/Controller/DeExternalAccountStatementsControllerTest.php new file mode 100644 index 0000000..6996ebf --- /dev/null +++ b/tests/TestCase/Controller/DeExternalAccountStatementsControllerTest.php @@ -0,0 +1,147 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeExternalAccountStatements', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $this->DeExternalAccountStatements = $this->getTableLocator()->get('DeExternalAccountStatements'); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeExternalAccountStatements); + + parent::tearDown(); + } + + /** + * Test index method + * + * Tests the index action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeExternalAccountStatementsController::index + */ + public function testIndexGetUnauthenticated(): void + { + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccountStatements', + 'action' => 'index', + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test index method + * + * Tests the index action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeExternalAccountStatementsController::index + */ + public function testIndexGetLoggedIn(): void + { + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccountStatements', + 'action' => 'index', + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test view method + * + * Tests the view action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeExternalAccountStatementsController::view + */ + public function testViewGetUnauthenticated(): void + { + $id = '2f83162b-2bec-4826-8853-fbda2fac3a95'; + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccountStatements', + 'action' => 'view', + $id, + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test view method + * + * Tests the view action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeExternalAccountStatementsController::view + */ + public function testViewGetLoggedIn(): void + { + $id = '2f83162b-2bec-4826-8853-fbda2fac3a95'; + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccountStatements', + 'action' => 'view', + $id, + ]; + $this->get($url); + $this->assertResponseCode(200); + } +} diff --git a/tests/TestCase/Controller/DeExternalAccountsControllerTest.php b/tests/TestCase/Controller/DeExternalAccountsControllerTest.php new file mode 100644 index 0000000..2769d00 --- /dev/null +++ b/tests/TestCase/Controller/DeExternalAccountsControllerTest.php @@ -0,0 +1,439 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeExternalAccounts', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $this->DeExternalAccounts = $this->getTableLocator()->get('DeExternalAccounts'); + $this->enableCsrfToken(); + $this->enableSecurityToken(); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeExternalAccounts); + + parent::tearDown(); + } + + /** + * Test index method + * + * Tests the index action with an unauthenticated user (not logged in) + * + * @uses \CakeAccounting\Controller\DeExternalAccountsController::index() + * @throws Exception + * + * @return void + */ + public function testIndexGetUnauthenticated(): void + { + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccounts', + 'action' => 'index', + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test index method + * + * Tests the index action with a logged in user + * + * @uses \CakeAccounting\Controller\DeExternalAccountsController::index() + * @throws Exception + * + * @return void + */ + public function testIndexGetLoggedIn(): void + { + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccounts', + 'action' => 'index', + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test view method + * + * Tests the view action with an unauthenticated user (not logged in) + * + * @uses \CakeAccounting\Controller\DeExternalAccountsController::view() + * @throws Exception + * + * @return void + */ + public function testViewGetUnauthenticated(): void + { + $id = '2463dc4f-6f38-4492-8c8e-918f11e13748'; + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccounts', + 'action' => 'view', + $id, + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test view method + * + * Tests the view action with a logged in user + * + * @uses \CakeAccounting\Controller\DeExternalAccountsController::view() + * @throws Exception + * + * @return void + */ + public function testViewGetLoggedIn(): void + { + $id = '2463dc4f-6f38-4492-8c8e-918f11e13748'; + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccounts', + 'action' => 'view', + $id, + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test add method + * + * Tests the add action with an unauthenticated user (not logged in) + * + * @uses \CakeAccounting\Controller\DeExternalAccountsController::add() + * @throws Exception + * + * @return void + */ + public function testAddGetUnauthenticated(): void + { + $cntBefore = $this->DeExternalAccounts->find()->count(); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccounts', + 'action' => 'add', + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + + $cntAfter = $this->DeExternalAccounts->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test add method + * + * Tests the add action with a logged in user + * + * @uses \CakeAccounting\Controller\DeExternalAccountsController::add() + * @throws Exception + * + * @return void + */ + public function testAddGetLoggedIn(): void + { + $cntBefore = $this->DeExternalAccounts->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccounts', + 'action' => 'add', + ]; + $this->get($url); + $this->assertResponseCode(200); + + $cntAfter = $this->DeExternalAccounts->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test add method + * + * Tests a POST request to the add action with a logged in user + * + * @uses \CakeAccounting\Controller\DeExternalAccountsController::add() + * @throws Exception + * + * @return void + */ + public function testAddPostLoggedInSuccess(): void + { + $cntBefore = $this->DeExternalAccounts->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccounts', + 'action' => 'add', + ]; + $data = [ + 'entity_type_code' => DE_ENTITY_TYPE_ORGANIZATION, + 'related_model' => '', + 'related_model_fk' => '', + ]; + $this->post($url, $data); + $this->assertResponseCode(302); + $this->assertRedirectContains('de-external-accounts'); + + $cntAfter = $this->DeExternalAccounts->find()->count(); + $this->assertEquals($cntBefore + 1, $cntAfter); + } + + /** + * Test add method + * + * Tests a POST request to the add action with a logged in user + * + * @uses \CakeAccounting\Controller\DeExternalAccountsController::add() + * @throws Exception + * + * @return void + */ + public function testAddPostLoggedInFailure(): void + { + $cntBefore = $this->DeExternalAccounts->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccounts', + 'action' => 'add', + ]; + $data = []; + $this->post($url, $data); + $this->assertResponseCode(200); + + $cntAfter = $this->DeExternalAccounts->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test edit method + * + * Tests the edit action with an unauthenticated user (not logged in) + * + * @uses \CakeAccounting\Controller\DeExternalAccountsController::edit() + * @throws Exception + * + * @return void + */ + public function testEditGetUnauthenticated(): void + { + $id = '2463dc4f-6f38-4492-8c8e-918f11e13748'; + + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccounts', + 'action' => 'edit', + $id, + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test edit method + * + * Tests the edit action with a logged in user + * + * @uses \CakeAccounting\Controller\DeExternalAccountsController::edit() + * @throws Exception + * + * @return void + */ + public function testEditGetLoggedIn(): void + { + $id = '2463dc4f-6f38-4492-8c8e-918f11e13748'; + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccounts', + 'action' => 'edit', + $id, + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test edit method + * + * Tests a PUT request to the edit action with a logged in user + * + * @uses \CakeAccounting\Controller\DeExternalAccountsController::edit() + * @throws Exception + * + * @return void + */ + public function testEditPutLoggedInSuccess(): void + { + $this->loginUserByRole('admin'); + $id = '2463dc4f-6f38-4492-8c8e-918f11e13748'; + $before = $this->DeExternalAccounts->get($id); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccounts', + 'action' => 'edit', + $id, + ]; + $data = [ + // test new data here + 'entity_type_code' => DE_ENTITY_TYPE_PROPERTY, + ]; + $this->put($url, $data); + + $this->assertResponseCode(302); + $this->assertRedirectContains('de-external-accounts'); + + $after = $this->DeExternalAccounts->get($id); + $this->assertEquals($data['entity_type_code'], $after->entity_type_code); + // assert saved properly below + } + + /** + * Test edit method + * + * Tests a PUT request to the edit action with a logged in user + * + * @uses \CakeAccounting\Controller\DeExternalAccountsController::edit() + * @throws Exception + * + * @return void + */ + public function testEditPutLoggedInFailure(): void + { + $this->loginUserByRole('admin'); + $id = '2463dc4f-6f38-4492-8c8e-918f11e13748'; + $before = $this->DeExternalAccounts->get($id); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccounts', + 'action' => 'edit', + $id, + ]; + $data = [ + 'entity_type_code' => '', + ]; + $this->put($url, $data); + $this->assertResponseCode(200); + $after = $this->DeExternalAccounts->get($id); + $this->assertEquals($before->entity_type_code, $after->entity_type_code); + // assert save failed below + } + + /** + * Test delete method + * + * Tests the delete action with an unauthenticated user (not logged in) + * + * @uses \CakeAccounting\Controller\DeExternalAccountsController::delete() + * @throws Exception + * + * @return void + */ + public function testDeleteUnauthenticated(): void + { + $cntBefore = $this->DeExternalAccounts->find()->count(); + $id = '2463dc4f-6f38-4492-8c8e-918f11e13748'; + + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccounts', + 'action' => 'delete', + $id, + ]; + $this->delete($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + + $cntAfter = $this->DeExternalAccounts->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test delete method + * + * Tests the delete action with a logged in user + * + * @uses \CakeAccounting\Controller\DeExternalAccountsController::delete() + * @throws Exception + * + * @return void + */ + public function testDeleteLoggedIn(): void + { + $cntBefore = $this->DeExternalAccounts->find()->count(); + $id = '2463dc4f-6f38-4492-8c8e-918f11e13748'; + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeExternalAccounts', + 'action' => 'delete', + $id, + ]; + $this->delete($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('de-external-accounts'); + + $cntAfter = $this->DeExternalAccounts->find()->count(); + $this->assertEquals($cntBefore - 1, $cntAfter); + } +} diff --git a/tests/TestCase/Controller/DeJournalEntriesControllerTest.php b/tests/TestCase/Controller/DeJournalEntriesControllerTest.php new file mode 100644 index 0000000..24b1170 --- /dev/null +++ b/tests/TestCase/Controller/DeJournalEntriesControllerTest.php @@ -0,0 +1,273 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeAccounts', + 'plugin.CakeAccounting.DeJournalEntries', + 'plugin.CakeAccounting.DeJournals', + 'plugin.CakeAccounting.DeJournalItems', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $this->DeJournalEntries = $this->getTableLocator()->get('DeJournalEntries'); + $this->enableCsrfToken(); + $this->enableSecurityToken(); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeJournalEntries); + + parent::tearDown(); + } + + /** + * Test index method + * + * Tests the index action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeJournalEntriesController::index + */ + public function testIndexGetUnauthenticated(): void + { + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournalEntries', + 'action' => 'index', + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test index method + * + * Tests the index action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeJournalEntriesController::index + */ + public function testIndexGetLoggedIn(): void + { + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournalEntries', + 'action' => 'index', + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test view method + * + * Tests the view action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeJournalEntriesController::view + */ + public function testViewGetUnauthenticated(): void + { + $id = 1; + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournalEntries', + 'action' => 'view', + $id, + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test view method + * + * Tests the view action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeJournalEntriesController::view + */ + public function testViewGetLoggedIn(): void + { + $id = 1; + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournalEntries', + 'action' => 'view', + $id, + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test add method + * + * Tests the add action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeJournalEntriesController::add + */ + public function testAddGetUnauthenticated(): void + { + $cntBefore = $this->DeJournalEntries->find()->count(); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournalEntries', + 'action' => 'add', + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + + $cntAfter = $this->DeJournalEntries->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test add method + * + * Tests the add action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeJournalEntriesController::add + */ + public function testAddGetLoggedIn(): void + { + $cntBefore = $this->DeJournalEntries->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournalEntries', + 'action' => 'add', + ]; + $this->get($url); + $this->assertResponseCode(200); + + $cntAfter = $this->DeJournalEntries->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test add method + * + * Tests a POST request to the add action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeJournalEntriesController::add + */ + public function testAddPostLoggedInSuccess(): void + { + $cntBefore = $this->DeJournalEntries->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournalEntries', + 'action' => 'add', + ]; + $data = [ + 'de_journal_id' => 1, + 'notes' => 'test notes', + 'de_journal_items' => [ + [ + 'account_number_credit' => 31000, + 'account_number_debit' => 11000, + 'external_account_number_credit' => '', + 'external_account_number_debit' => '', + 'amount' => 500, + ], + ], + ]; + $this->post($url, $data); + $this->assertResponseCode(302); + $this->assertRedirectContains('de-journal-entries'); + + $cntAfter = $this->DeJournalEntries->find()->count(); + $this->assertEquals($cntBefore + 1, $cntAfter); + } + + /** + * Test add method + * + * Tests a POST request to the add action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeJournalEntriesController::add + */ + public function testAddPostLoggedInFailure(): void + { + $cntBefore = $this->DeJournalEntries->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournalEntries', + 'action' => 'add', + ]; + $data = []; + $this->post($url, $data); + $this->assertResponseCode(200); + + $cntAfter = $this->DeJournalEntries->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } +} diff --git a/tests/TestCase/Controller/DeJournalItemsControllerTest.php b/tests/TestCase/Controller/DeJournalItemsControllerTest.php new file mode 100644 index 0000000..05055fa --- /dev/null +++ b/tests/TestCase/Controller/DeJournalItemsControllerTest.php @@ -0,0 +1,155 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeJournals', + 'plugin.CakeAccounting.DeJournalEntries', + 'plugin.CakeAccounting.DeJournalItems', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $this->DeJournalItems = $this->getTableLocator()->get('DeJournalItems'); + $this->DeAccounts = $this->getTableLocator()->get('DeAccounts'); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeJournalItems); + + parent::tearDown(); + } + + /** + * Test index method + * + * Tests the index action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeJournalItemsController::index + */ + public function testIndexGetUnauthenticated(): void + { + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournalItems', + 'action' => 'index', + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test index method + * + * Tests the index action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeJournalItemsController::index + */ + public function testIndexGetLoggedIn(): void + { + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournalItems', + 'action' => 'index', + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test view method + * + * Tests the view action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeJournalItemsController::view + */ + public function testViewGetUnauthenticated(): void + { + $id = 1; + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournalItems', + 'action' => 'view', + $id, + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test view method + * + * Tests the view action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeJournalItemsController::view + */ + public function testViewGetLoggedIn(): void + { + $id = 1; + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournalItems', + 'action' => 'view', + $id, + ]; + $this->get($url); + $this->assertResponseCode(200); + } +} diff --git a/tests/TestCase/Controller/DeJournalsControllerTest.php b/tests/TestCase/Controller/DeJournalsControllerTest.php new file mode 100644 index 0000000..68acfcd --- /dev/null +++ b/tests/TestCase/Controller/DeJournalsControllerTest.php @@ -0,0 +1,452 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeJournals', + 'plugin.CakeAccounting.DeJournalEntries', + 'plugin.CakeAccounting.DeJournalItems', + 'plugin.CakeAccounting.DeJournalTypes', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $this->DeJournals = $this->getTableLocator()->get('DeJournals'); + $this->enableCsrfToken(); + $this->enableSecurityToken(); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeJournals); + + parent::tearDown(); + } + + /** + * Test index method + * + * Tests the index action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeJournalsController::index + */ + public function testIndexGetUnauthenticated(): void + { + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournals', + 'action' => 'index', + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test index method + * + * Tests the index action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeJournalsController::index + */ + public function testIndexGetLoggedIn(): void + { + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournals', + 'action' => 'index', + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test view method + * + * Tests the view action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeJournalsController::view + */ + public function testViewGetUnauthenticated(): void + { + $id = 1; + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournals', + 'action' => 'view', + $id, + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test view method + * + * Tests the view action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeJournalsController::view + */ + public function testViewGetLoggedIn(): void + { + $id = 1; + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournals', + 'action' => 'view', + $id, + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test add method + * + * Tests the add action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeJournalsController::add + */ + public function testAddGetUnauthenticated(): void + { + $cntBefore = $this->DeJournals->find()->count(); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournals', + 'action' => 'add', + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + + $cntAfter = $this->DeJournals->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test add method + * + * Tests the add action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeJournalsController::add + */ + public function testAddGetLoggedIn(): void + { + $cntBefore = $this->DeJournals->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournals', + 'action' => 'add', + ]; + $this->get($url); + $this->assertResponseCode(200); + + $cntAfter = $this->DeJournals->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test add method + * + * Tests a POST request to the add action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeJournalsController::add + */ + public function testAddPostLoggedInSuccess(): void + { + $cntBefore = $this->DeJournals->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournals', + 'action' => 'add', + ]; + $data = [ + 'name' => 'new journal', + 'short_code' => 'NEW', + 'default_account_number_credit' => '', + 'default_account_number_debit' => '', + 'de_journal_type_code' => DE_JOURNAL_TYPE_MISC, + ]; + $this->post($url, $data); + $this->assertResponseCode(302); + $this->assertRedirectContains('de-journals'); + + $cntAfter = $this->DeJournals->find()->count(); + $this->assertEquals($cntBefore + 1, $cntAfter); + } + + /** + * Test add method + * + * Tests a POST request to the add action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeJournalsController::add + */ + public function testAddPostLoggedInFailure(): void + { + $cntBefore = $this->DeJournals->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournals', + 'action' => 'add', + ]; + $data = [ + 'name' => 'new journal', + 'short_code' => '', + 'default_account_number_credit' => '9999999999', + 'default_account_number_debit' => '999999999999', + 'de_journal_type_code' => 'not real code', + ]; + $this->post($url, $data); + $this->assertResponseCode(200); + + $cntAfter = $this->DeJournals->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test edit method + * + * Tests the edit action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeJournalsController::edit + */ + public function testEditGetUnauthenticated(): void + { + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournals', + 'action' => 'edit', + 1, + ]; + $this->get($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + } + + /** + * Test edit method + * + * Tests the edit action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeJournalsController::edit + */ + public function testEditGetLoggedIn(): void + { + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournals', + 'action' => 'edit', + 1, + ]; + $this->get($url); + $this->assertResponseCode(200); + } + + /** + * Test edit method + * + * Tests a PUT request to the edit action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeJournalsController::edit + */ + public function testEditPutLoggedInSuccess(): void + { + $this->loginUserByRole('admin'); + $id = 1; + $before = $this->DeJournals->get($id); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournals', + 'action' => 'edit', + $id, + ]; + $data = [ + // test new data here + ]; + $this->put($url, $data); + + $this->assertResponseCode(302); + $this->assertRedirectContains('de-journals'); + + $after = $this->DeJournals->get($id); + // assert saved properly below + } + + /** + * Test edit method + * + * Tests a PUT request to the edit action with a logged in user + * + * @return void + * @throws Exception + * + * @uses DeJournalsController::edit + */ + public function testEditPutLoggedInFailure(): void + { + $this->loginUserByRole('admin'); + $id = 1; + $before = $this->DeJournals->get($id); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournals', + 'action' => 'edit', + $id, + ]; + $data = [ + 'name' => 'new journal', + 'short_code' => '', + 'default_account_number_credit' => '9999999999', + 'default_account_number_debit' => '999999999999', + 'de_journal_type_code' => 'not real code', + ]; + $this->put($url, $data); + $this->assertResponseCode(200); + + $after = $this->DeJournals->get($id); + // assert save failed below + $this->assertEquals($before->name, $after->name); + $this->assertEquals($before->short_code, $after->short_code); + $this->assertEquals($before->name, $after->name); + $this->assertEquals($before->default_account_number_credit, $after->default_account_number_credit); + $this->assertEquals($before->default_account_number_debit, $after->default_account_number_debit); + $this->assertEquals($before->de_journal_type_code, $after->de_journal_type_code); + } + + /** + * Test delete method + * + * Tests the delete action with an unauthenticated user (not logged in) + * + * @return void + * @throws Exception + * + * @uses DeJournalsController::delete + */ + public function testDeleteUnauthenticated(): void + { + $cntBefore = $this->DeJournals->find()->count(); + + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournals', + 'action' => 'delete', + 1, + ]; + $this->delete($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('login'); + + $cntAfter = $this->DeJournals->find()->count(); + $this->assertEquals($cntBefore, $cntAfter); + } + + /** + * Test delete method + * + * Tests the delete action with a logged in user + * + * @return void + *@throws Exception + * + * @uses DeJournalsController::delete + */ + public function testDeleteLoggedIn(): void + { + $cntBefore = $this->DeJournals->find()->count(); + + $this->loginUserByRole('admin'); + $url = [ + 'plugin' => 'CakeAccounting', + 'controller' => 'DeJournals', + 'action' => 'delete', + 1, + ]; + $this->delete($url); + $this->assertResponseCode(302); + $this->assertRedirectContains('de-journals'); + + $cntAfter = $this->DeJournals->find()->count(); + $this->assertEquals($cntBefore - 1, $cntAfter); + } +} diff --git a/tests/TestCase/Model/Table/DeAccountStatementsTableTest.php b/tests/TestCase/Model/Table/DeAccountStatementsTableTest.php new file mode 100644 index 0000000..5dcb45d --- /dev/null +++ b/tests/TestCase/Model/Table/DeAccountStatementsTableTest.php @@ -0,0 +1,93 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeAccountStatements', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $config = $this->getTableLocator()->exists('DeAccountStatements') ? [] : ['className' => DeAccountStatementsTable::class]; + $this->DeAccountStatements = $this->getTableLocator()->get('DeAccountStatements', $config); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeAccountStatements); + + parent::tearDown(); + } + + /** + * TestInitialize method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeAccountStatementsTable::initialize() + */ + public function testInitialize(): void + { + // verify all associations loaded + $expectedAssociations = [ + 'DeAccounts', + ]; + $associations = $this->DeAccountStatements->associations(); + + $this->assertCount(count($expectedAssociations), $associations); + foreach ($expectedAssociations as $expectedAssociation) { + $this->assertTrue($this->DeAccountStatements->hasAssociation($expectedAssociation)); + } + + // verify all behaviors loaded + $expectedBehaviors = []; + $behaviors = $this->DeAccountStatements->behaviors(); + + $this->assertCount(count($expectedBehaviors), $behaviors); + foreach ($expectedBehaviors as $expectedBehavior) { + $this->assertTrue($this->DeAccountStatements->hasBehavior($expectedBehavior)); + } + } + + /** + * Test validationDefault method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeAccountStatementsTable::validationDefault() + */ + public function testValidationDefault(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } +} diff --git a/tests/TestCase/Model/Table/DeAccountTemplatesTableTest.php b/tests/TestCase/Model/Table/DeAccountTemplatesTableTest.php new file mode 100644 index 0000000..010372f --- /dev/null +++ b/tests/TestCase/Model/Table/DeAccountTemplatesTableTest.php @@ -0,0 +1,93 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeAccountTemplates', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $config = $this->getTableLocator()->exists('DeAccountTemplates') ? [] : ['className' => DeAccountTemplatesTable::class]; + $this->DeAccountTemplates = $this->getTableLocator()->get('DeAccountTemplates', $config); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeAccountTemplates); + + parent::tearDown(); + } + + /** + * TestInitialize method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeAccountTemplatesTable::initialize() + */ + public function testInitialize(): void + { + // verify all associations loaded + $expectedAssociations = [ + 'DeTemplatedAccounts', + ]; + $associations = $this->DeAccountTemplates->associations(); + + $this->assertCount(count($expectedAssociations), $associations); + foreach ($expectedAssociations as $expectedAssociation) { + $this->assertTrue($this->DeAccountTemplates->hasAssociation($expectedAssociation)); + } + + // verify all behaviors loaded + $expectedBehaviors = []; + $behaviors = $this->DeAccountTemplates->behaviors(); + + $this->assertCount(count($expectedBehaviors), $behaviors); + foreach ($expectedBehaviors as $expectedBehavior) { + $this->assertTrue($this->DeAccountTemplates->hasBehavior($expectedBehavior)); + } + } + + /** + * Test validationDefault method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeAccountTemplatesTable::validationDefault() + */ + public function testValidationDefault(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } +} diff --git a/tests/TestCase/Model/Table/DeAccountTypesTableTest.php b/tests/TestCase/Model/Table/DeAccountTypesTableTest.php new file mode 100644 index 0000000..0acc38d --- /dev/null +++ b/tests/TestCase/Model/Table/DeAccountTypesTableTest.php @@ -0,0 +1,91 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeAccountTypes', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $config = $this->getTableLocator()->exists('DeAccountTypes') ? [] : ['className' => DeAccountTypesTable::class]; + $this->DeAccountTypes = $this->getTableLocator()->get('DeAccountTypes', $config); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeAccountTypes); + + parent::tearDown(); + } + + /** + * TestInitialize method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeAccountTypesTable::initialize() + */ + public function testInitialize(): void + { + // verify all associations loaded + $expectedAssociations = []; + $associations = $this->DeAccountTypes->associations(); + + $this->assertCount(count($expectedAssociations), $associations); + foreach ($expectedAssociations as $expectedAssociation) { + $this->assertTrue($this->DeAccountTypes->hasAssociation($expectedAssociation)); + } + + // verify all behaviors loaded + $expectedBehaviors = []; + $behaviors = $this->DeAccountTypes->behaviors(); + + $this->assertCount(count($expectedBehaviors), $behaviors); + foreach ($expectedBehaviors as $expectedBehavior) { + $this->assertTrue($this->DeAccountTypes->hasBehavior($expectedBehavior)); + } + } + + /** + * Test validationDefault method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeAccountTypesTable::validationDefault() + */ + public function testValidationDefault(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } +} diff --git a/tests/TestCase/Model/Table/DeAccountsTableTest.php b/tests/TestCase/Model/Table/DeAccountsTableTest.php new file mode 100644 index 0000000..47df067 --- /dev/null +++ b/tests/TestCase/Model/Table/DeAccountsTableTest.php @@ -0,0 +1,111 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeAccounts', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $config = $this->getTableLocator()->exists('DeAccounts') ? [] : ['className' => DeAccountsTable::class]; + $this->DeAccounts = $this->getTableLocator()->get('DeAccounts', $config); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeAccounts); + + parent::tearDown(); + } + + /** + * TestInitialize method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeAccountsTable::initialize() + */ + public function testInitialize(): void + { + // verify all associations loaded + $expectedAssociations = [ + 'ParentDeAccounts', + 'ChildDeAccounts', + 'DeAccountTypes', + 'DeAccountStatements', + 'DebitDeJournalItems', + 'CreditDeJournalItems', + ]; + $associations = $this->DeAccounts->associations(); + + $this->assertCount(count($expectedAssociations), $associations); + foreach ($expectedAssociations as $expectedAssociation) { + $this->assertTrue($this->DeAccounts->hasAssociation($expectedAssociation)); + } + + // verify all behaviors loaded + $expectedBehaviors = [ + 'Tree', + ]; + $behaviors = $this->DeAccounts->behaviors(); + + $this->assertCount(count($expectedBehaviors), $behaviors); + foreach ($expectedBehaviors as $expectedBehavior) { + $this->assertTrue($this->DeAccounts->hasBehavior($expectedBehavior)); + } + } + + /** + * Test validationDefault method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeAccountsTable::validationDefault() + */ + public function testValidationDefault(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } + + /** + * Test buildRules method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeAccountsTable::buildRules() + */ + public function testBuildRules(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } +} diff --git a/tests/TestCase/Model/Table/DeBankAccountTypesTableTest.php b/tests/TestCase/Model/Table/DeBankAccountTypesTableTest.php new file mode 100644 index 0000000..878ada9 --- /dev/null +++ b/tests/TestCase/Model/Table/DeBankAccountTypesTableTest.php @@ -0,0 +1,103 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeBankAccountTypes', + 'plugin.CakeAccounting.DeBaseTypes', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $config = $this->getTableLocator()->exists('DeBankAccountTypes') ? [] : ['className' => DeBankAccountTypesTable::class]; + $this->DeBankAccountTypes = $this->getTableLocator()->get('DeBankAccountTypes', $config); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeBankAccountTypes); + + parent::tearDown(); + } + + /** + * TestInitialize method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeBankAccountTypesTable::initialize() + */ + public function testInitialize(): void + { + // verify all associations loaded + $expectedAssociations = []; + $associations = $this->DeBankAccountTypes->associations(); + + $this->assertCount(count($expectedAssociations), $associations); + foreach ($expectedAssociations as $expectedAssociation) { + $this->assertTrue($this->DeBankAccountTypes->hasAssociation($expectedAssociation)); + } + + // verify all behaviors loaded + $expectedBehaviors = []; + $behaviors = $this->DeBankAccountTypes->behaviors(); + + $this->assertCount(count($expectedBehaviors), $behaviors); + foreach ($expectedBehaviors as $expectedBehavior) { + $this->assertTrue($this->DeBankAccountTypes->hasBehavior($expectedBehavior)); + } + } + + /** + * Test validationDefault method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeBankAccountTypesTable::validationDefault() + */ + public function testValidationDefault(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } + + /** + * Test buildRules method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeBankAccountTypesTable::buildRules() + */ + public function testBuildRules(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } +} diff --git a/tests/TestCase/Model/Table/DeBaseTypesTableTest.php b/tests/TestCase/Model/Table/DeBaseTypesTableTest.php new file mode 100644 index 0000000..cdaed03 --- /dev/null +++ b/tests/TestCase/Model/Table/DeBaseTypesTableTest.php @@ -0,0 +1,91 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeBaseTypes', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $config = $this->getTableLocator()->exists('DeBaseTypes') ? [] : ['className' => DeBaseTypesTable::class]; + $this->DeBaseTypes = $this->getTableLocator()->get('DeBaseTypes', $config); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeBaseTypes); + + parent::tearDown(); + } + + /** + * TestInitialize method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeBaseTypesTable::initialize() + */ + public function testInitialize(): void + { + // verify all associations loaded + $expectedAssociations = []; + $associations = $this->DeBaseTypes->associations(); + + $this->assertCount(count($expectedAssociations), $associations); + foreach ($expectedAssociations as $expectedAssociation) { + $this->assertTrue($this->DeBaseTypes->hasAssociation($expectedAssociation)); + } + + // verify all behaviors loaded + $expectedBehaviors = []; + $behaviors = $this->DeBaseTypes->behaviors(); + + $this->assertCount(count($expectedBehaviors), $behaviors); + foreach ($expectedBehaviors as $expectedBehavior) { + $this->assertTrue($this->DeBaseTypes->hasBehavior($expectedBehavior)); + } + } + + /** + * Test validationDefault method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeBaseTypesTable::validationDefault() + */ + public function testValidationDefault(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } +} diff --git a/tests/TestCase/Model/Table/DeEntityTypesTableTest.php b/tests/TestCase/Model/Table/DeEntityTypesTableTest.php new file mode 100644 index 0000000..7f2835e --- /dev/null +++ b/tests/TestCase/Model/Table/DeEntityTypesTableTest.php @@ -0,0 +1,91 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeEntityTypes', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $config = $this->getTableLocator()->exists('DeEntityTypes') ? [] : ['className' => DeEntityTypesTable::class]; + $this->DeEntityTypes = $this->getTableLocator()->get('DeEntityTypes', $config); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeEntityTypes); + + parent::tearDown(); + } + + /** + * TestInitialize method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeEntityTypesTable::initialize() + */ + public function testInitialize(): void + { + // verify all associations loaded + $expectedAssociations = []; + $associations = $this->DeEntityTypes->associations(); + + $this->assertCount(count($expectedAssociations), $associations); + foreach ($expectedAssociations as $expectedAssociation) { + $this->assertTrue($this->DeEntityTypes->hasAssociation($expectedAssociation)); + } + + // verify all behaviors loaded + $expectedBehaviors = []; + $behaviors = $this->DeEntityTypes->behaviors(); + + $this->assertCount(count($expectedBehaviors), $behaviors); + foreach ($expectedBehaviors as $expectedBehavior) { + $this->assertTrue($this->DeEntityTypes->hasBehavior($expectedBehavior)); + } + } + + /** + * Test validationDefault method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeEntityTypesTable::validationDefault() + */ + public function testValidationDefault(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } +} diff --git a/tests/TestCase/Model/Table/DeExternalAccountStatementsTableTest.php b/tests/TestCase/Model/Table/DeExternalAccountStatementsTableTest.php new file mode 100644 index 0000000..8879945 --- /dev/null +++ b/tests/TestCase/Model/Table/DeExternalAccountStatementsTableTest.php @@ -0,0 +1,93 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeExternalAccountStatements', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $config = $this->getTableLocator()->exists('DeExternalAccountStatements') ? [] : ['className' => DeExternalAccountStatementsTable::class]; + $this->DeExternalAccountStatements = $this->getTableLocator()->get('DeExternalAccountStatements', $config); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeExternalAccountStatements); + + parent::tearDown(); + } + + /** + * TestInitialize method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeExternalAccountStatementsTable::initialize() + */ + public function testInitialize(): void + { + // verify all associations loaded + $expectedAssociations = [ + 'DeExternalAccounts', + ]; + $associations = $this->DeExternalAccountStatements->associations(); + + $this->assertCount(count($expectedAssociations), $associations); + foreach ($expectedAssociations as $expectedAssociation) { + $this->assertTrue($this->DeExternalAccountStatements->hasAssociation($expectedAssociation)); + } + + // verify all behaviors loaded + $expectedBehaviors = []; + $behaviors = $this->DeExternalAccountStatements->behaviors(); + + $this->assertCount(count($expectedBehaviors), $behaviors); + foreach ($expectedBehaviors as $expectedBehavior) { + $this->assertTrue($this->DeExternalAccountStatements->hasBehavior($expectedBehavior)); + } + } + + /** + * Test validationDefault method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeExternalAccountStatementsTable::validationDefault() + */ + public function testValidationDefault(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } +} diff --git a/tests/TestCase/Model/Table/DeExternalAccountsTableTest.php b/tests/TestCase/Model/Table/DeExternalAccountsTableTest.php new file mode 100644 index 0000000..ec8d19e --- /dev/null +++ b/tests/TestCase/Model/Table/DeExternalAccountsTableTest.php @@ -0,0 +1,96 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeExternalAccounts', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $config = $this->getTableLocator()->exists('DeExternalAccounts') ? [] : ['className' => DeExternalAccountsTable::class]; + $this->DeExternalAccounts = $this->getTableLocator()->get('DeExternalAccounts', $config); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeExternalAccounts); + + parent::tearDown(); + } + + /** + * TestInitialize method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeExternalAccountsTable::initialize() + */ + public function testInitialize(): void + { + // verify all associations loaded + $expectedAssociations = [ + 'DeExternalAccountStatements', + 'DeEntityTypes', + ]; + $associations = $this->DeExternalAccounts->associations(); + + $this->assertCount(count($expectedAssociations), $associations); + foreach ($expectedAssociations as $expectedAssociation) { + $this->assertTrue($this->DeExternalAccounts->hasAssociation($expectedAssociation)); + } + + // verify all behaviors loaded + $expectedBehaviors = [ + 'Timestamp', + ]; + $behaviors = $this->DeExternalAccounts->behaviors(); + + $this->assertCount(count($expectedBehaviors), $behaviors); + foreach ($expectedBehaviors as $expectedBehavior) { + $this->assertTrue($this->DeExternalAccounts->hasBehavior($expectedBehavior)); + } + } + + /** + * Test validationDefault method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeExternalAccountsTable::validationDefault() + */ + public function testValidationDefault(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } +} diff --git a/tests/TestCase/Model/Table/DeJournalEntriesTableTest.php b/tests/TestCase/Model/Table/DeJournalEntriesTableTest.php new file mode 100644 index 0000000..35852b9 --- /dev/null +++ b/tests/TestCase/Model/Table/DeJournalEntriesTableTest.php @@ -0,0 +1,110 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeJournalEntries', + 'plugin.CakeAccounting.DeJournals', +// 'plugin.CakeAccounting.Users', + 'plugin.CakeAccounting.DeJournalItems', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $config = $this->getTableLocator()->exists('DeJournalEntries') ? [] : ['className' => DeJournalEntriesTable::class]; + $this->DeJournalEntries = $this->getTableLocator()->get('DeJournalEntries', $config); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeJournalEntries); + + parent::tearDown(); + } + + /** + * TestInitialize method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeJournalEntriesTable::initialize() + */ + public function testInitialize(): void + { + // verify all associations loaded + $expectedAssociations = [ + 'DeJournalItems', + 'DeJournals', + ]; + $associations = $this->DeJournalEntries->associations(); + + $this->assertCount(count($expectedAssociations), $associations); + foreach ($expectedAssociations as $expectedAssociation) { + $this->assertTrue($this->DeJournalEntries->hasAssociation($expectedAssociation)); + } + + // verify all behaviors loaded + $expectedBehaviors = [ + 'Timestamp', + ]; + $behaviors = $this->DeJournalEntries->behaviors(); + + $this->assertCount(count($expectedBehaviors), $behaviors); + foreach ($expectedBehaviors as $expectedBehavior) { + $this->assertTrue($this->DeJournalEntries->hasBehavior($expectedBehavior)); + } + } + + /** + * Test validationDefault method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeJournalEntriesTable::validationDefault() + */ + public function testValidationDefault(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } + + /** + * Test buildRules method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeJournalEntriesTable::buildRules() + */ + public function testBuildRules(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } +} diff --git a/tests/TestCase/Model/Table/DeJournalItemsTableTest.php b/tests/TestCase/Model/Table/DeJournalItemsTableTest.php new file mode 100644 index 0000000..651dc15 --- /dev/null +++ b/tests/TestCase/Model/Table/DeJournalItemsTableTest.php @@ -0,0 +1,111 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeJournalItems', + 'plugin.CakeAccounting.DeJournalEntries', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $config = $this->getTableLocator()->exists('DeJournalItems') ? [] : ['className' => DeJournalItemsTable::class]; + $this->DeJournalItems = $this->getTableLocator()->get('DeJournalItems', $config); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeJournalItems); + + parent::tearDown(); + } + + /** + * TestInitialize method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeJournalItemsTable::initialize() + */ + public function testInitialize(): void + { + // verify all associations loaded + $expectedAssociations = [ + 'DeJournalEntries', + 'CreditDeAccounts', + 'DebitDeAccounts', + 'CreditDeExternalAccounts', + 'DebitDeExternalAccounts', + ]; + $associations = $this->DeJournalItems->associations(); + + $this->assertCount(count($expectedAssociations), $associations); + foreach ($expectedAssociations as $expectedAssociation) { + $this->assertTrue($this->DeJournalItems->hasAssociation($expectedAssociation)); + } + + // verify all behaviors loaded + $expectedBehaviors = [ + 'Timestamp', + ]; + $behaviors = $this->DeJournalItems->behaviors(); + + $this->assertCount(count($expectedBehaviors), $behaviors); + foreach ($expectedBehaviors as $expectedBehavior) { + $this->assertTrue($this->DeJournalItems->hasBehavior($expectedBehavior)); + } + } + + /** + * Test validationDefault method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeJournalItemsTable::validationDefault() + */ + public function testValidationDefault(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } + + /** + * Test buildRules method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeJournalItemsTable::buildRules() + */ + public function testBuildRules(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } +} diff --git a/tests/TestCase/Model/Table/DeJournalTypesTableTest.php b/tests/TestCase/Model/Table/DeJournalTypesTableTest.php new file mode 100644 index 0000000..2c52a01 --- /dev/null +++ b/tests/TestCase/Model/Table/DeJournalTypesTableTest.php @@ -0,0 +1,80 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeJournalTypes', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $config = $this->getTableLocator()->exists('DeJournalTypes') ? [] : ['className' => DeJournalTypesTable::class]; + $this->DeJournalTypes = $this->getTableLocator()->get('DeJournalTypes', $config); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeJournalTypes); + + parent::tearDown(); + } + /** + * TestInitialize method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeJournalTypesTable::initialize() + */ + public function testInitialize(): void + { + // verify all associations loaded + $expectedAssociations = []; + $associations = $this->DeJournalTypes->associations(); + + $this->assertCount(count($expectedAssociations), $associations); + foreach ($expectedAssociations as $expectedAssociation) { + $this->assertTrue($this->DeJournalTypes->hasAssociation($expectedAssociation)); + } + + // verify all behaviors loaded + $expectedBehaviors = []; + $behaviors = $this->DeJournalTypes->behaviors(); + + $this->assertCount(count($expectedBehaviors), $behaviors); + foreach ($expectedBehaviors as $expectedBehavior) { + $this->assertTrue($this->DeJournalTypes->hasBehavior($expectedBehavior)); + } + } + +} diff --git a/tests/TestCase/Model/Table/DeJournalsTableTest.php b/tests/TestCase/Model/Table/DeJournalsTableTest.php new file mode 100644 index 0000000..47c563f --- /dev/null +++ b/tests/TestCase/Model/Table/DeJournalsTableTest.php @@ -0,0 +1,95 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeJournals', + 'plugin.CakeAccounting.DeJournalEntries', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $config = $this->getTableLocator()->exists('DeJournals') ? [] : ['className' => DeJournalsTable::class]; + $this->DeJournals = $this->getTableLocator()->get('DeJournals', $config); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeJournals); + + parent::tearDown(); + } + + /** + * TestInitialize method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeJournalsTable::initialize() + */ + public function testInitialize(): void + { + // verify all associations loaded + $expectedAssociations = [ + 'DeJournalTypes', + 'DeJournalEntries', + ]; + $associations = $this->DeJournals->associations(); + + $this->assertCount(count($expectedAssociations), $associations); + foreach ($expectedAssociations as $expectedAssociation) { + $this->assertTrue($this->DeJournals->hasAssociation($expectedAssociation)); + } + + // verify all behaviors loaded + $expectedBehaviors = []; + $behaviors = $this->DeJournals->behaviors(); + + $this->assertCount(count($expectedBehaviors), $behaviors); + foreach ($expectedBehaviors as $expectedBehavior) { + $this->assertTrue($this->DeJournals->hasBehavior($expectedBehavior)); + } + } + + /** + * Test validationDefault method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeJournalsTable::validationDefault() + */ + public function testValidationDefault(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } +} diff --git a/tests/TestCase/Model/Table/DeTemplatedAccountsTableTest.php b/tests/TestCase/Model/Table/DeTemplatedAccountsTableTest.php new file mode 100644 index 0000000..591bae0 --- /dev/null +++ b/tests/TestCase/Model/Table/DeTemplatedAccountsTableTest.php @@ -0,0 +1,109 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeTemplatedAccounts', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $config = $this->getTableLocator()->exists('DeTemplatedAccounts') ? [] : ['className' => DeTemplatedAccountsTable::class]; + $this->DeTemplatedAccounts = $this->getTableLocator()->get('DeTemplatedAccounts', $config); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeTemplatedAccounts); + + parent::tearDown(); + } + + /** + * TestInitialize method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeTemplatedAccountsTable::initialize() + */ + public function testInitialize(): void + { + // verify all associations loaded + $expectedAssociations = [ + 'ParentDeTemplatedAccounts', + 'ChildDeTemplatedAccounts', + 'DeAccountTemplates', + 'DeAccountTypes', + ]; + $associations = $this->DeTemplatedAccounts->associations(); + + $this->assertCount(count($expectedAssociations), $associations); + foreach ($expectedAssociations as $expectedAssociation) { + $this->assertTrue($this->DeTemplatedAccounts->hasAssociation($expectedAssociation)); + } + + // verify all behaviors loaded + $expectedBehaviors = [ + 'Tree', + ]; + $behaviors = $this->DeTemplatedAccounts->behaviors(); + + $this->assertCount(count($expectedBehaviors), $behaviors); + foreach ($expectedBehaviors as $expectedBehavior) { + $this->assertTrue($this->DeTemplatedAccounts->hasBehavior($expectedBehavior)); + } + } + + /** + * Test validationDefault method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeTemplatedAccountsTable::validationDefault() + */ + public function testValidationDefault(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } + + /** + * Test buildRules method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeTemplatedAccountsTable::buildRules() + */ + public function testBuildRules(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } +} diff --git a/tests/TestCase/Model/Table/DeTransactionTypesTableTest.php b/tests/TestCase/Model/Table/DeTransactionTypesTableTest.php new file mode 100644 index 0000000..beccfc0 --- /dev/null +++ b/tests/TestCase/Model/Table/DeTransactionTypesTableTest.php @@ -0,0 +1,91 @@ + + */ + protected array $fixtures = [ + 'plugin.CakeAccounting.DeTransactionTypes', + ]; + + /** + * setUp method + * + * @return void + */ + protected function setUp(): void + { + parent::setUp(); + $config = $this->getTableLocator()->exists('DeTransactionTypes') ? [] : ['className' => DeTransactionTypesTable::class]; + $this->DeTransactionTypes = $this->getTableLocator()->get('DeTransactionTypes', $config); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->DeTransactionTypes); + + parent::tearDown(); + } + + /** + * TestInitialize method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeTransactionTypesTable::initialize() + */ + public function testInitialize(): void + { + // verify all associations loaded + $expectedAssociations = []; + $associations = $this->DeTransactionTypes->associations(); + + $this->assertCount(count($expectedAssociations), $associations); + foreach ($expectedAssociations as $expectedAssociation) { + $this->assertTrue($this->DeTransactionTypes->hasAssociation($expectedAssociation)); + } + + // verify all behaviors loaded + $expectedBehaviors = []; + $behaviors = $this->DeTransactionTypes->behaviors(); + + $this->assertCount(count($expectedBehaviors), $behaviors); + foreach ($expectedBehaviors as $expectedBehavior) { + $this->assertTrue($this->DeTransactionTypes->hasBehavior($expectedBehavior)); + } + } + + /** + * Test validationDefault method + * + * @return void + * @uses \CakeAccounting\Model\Table\DeTransactionTypesTable::validationDefault() + */ + public function testValidationDefault(): void + { + $this->markTestIncomplete('Not implemented yet.'); + } +} diff --git a/tests/TestCase/View/Helper/AccountingHelperTest.php b/tests/TestCase/View/Helper/AccountingHelperTest.php new file mode 100644 index 0000000..0af36fd --- /dev/null +++ b/tests/TestCase/View/Helper/AccountingHelperTest.php @@ -0,0 +1,45 @@ +Accounting = new AccountingHelper($view); + } + + /** + * tearDown method + * + * @return void + */ + protected function tearDown(): void + { + unset($this->Accounting); + + parent::tearDown(); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..7165d4b --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,60 @@ +runMany([ + // Run app migrations on test connection. + ['connection' => 'test'], + // Run plugin migrations on test connection. + ['plugin' => 'CakeAccounting'], +]); diff --git a/tests/schema.sql b/tests/schema.sql new file mode 100644 index 0000000..4f4127e --- /dev/null +++ b/tests/schema.sql @@ -0,0 +1 @@ +-- Test database schema for CakeAccounting diff --git a/webroot/.gitkeep b/webroot/.gitkeep new file mode 100644 index 0000000..e69de29