diff --git a/.github/dev_bug_report.md b/.github/dev_bug_report.md deleted file mode 100644 index ef93e56..0000000 --- a/.github/dev_bug_report.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: Dev Bug Report -about: Create a report to help us improve -title: '' -labels: stat_backlog, type_bug -assignees: '' - ---- - -# Bug Description -A clear and concise description of what the bug is. - -# How to Reproduce - -Steps to reproduce the behavior: - -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -## Minimal Code Example - -``` -// your code ... -``` - -# Expected Behavior -A clear and concise description of what you expected to happen. - -# Screenshots -If applicable, add screenshots to help explain your problem. - -# Additional Information -Add any other context about the problem here. diff --git a/.github/dev_feature_request.md b/.github/dev_feature_request.md deleted file mode 100644 index 9573c35..0000000 --- a/.github/dev_feature_request.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: Dev Feature Request -about: Suggest an idea for this project -title: '' -labels: stat_backlog, type_feature -assignees: '' - ---- - -# What is the feature you request -* A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -* A clear and concise description of what you want to happen. - -# Alternatives -A clear and concise description of any alternative solutions or features you've considered. - -# Additional Information -Add any other context or screenshots about the feature request here. diff --git a/Admin/Install/Coa/SKR03_DE_GAAP_SALES_COST.json b/Admin/Install/Coa/SKR03_DE_GAAP_SALES_COST.json index 43e6fab..0aefed6 100644 --- a/Admin/Install/Coa/SKR03_DE_GAAP_SALES_COST.json +++ b/Admin/Install/Coa/SKR03_DE_GAAP_SALES_COST.json @@ -35,6 +35,18 @@ "style": "subtotal", "children": [] }, + { + "name": "GPratio", + "l11n": { + "en": "Gross profit %", + "de": "Rohertragsmarge %" + }, + "account": [], + "type": "formula", + "formula": "3/1", + "style": "ratio", + "children": [] + }, { "name": "4", "l11n": { @@ -267,5 +279,17 @@ "formula": "14+15", "style": "total", "children": [] + }, + { + "name": "ProfitRatio", + "l11n": { + "en": "Net profit %", + "de": "Jahresergebnis %" + }, + "account": [], + "type": "formula", + "formula": "(14+15)/1", + "style": "ratio", + "children": [] } ] \ No newline at end of file diff --git a/Admin/Install/Coa/SKR03_DE_GAAP_TOTAL_COST.json b/Admin/Install/Coa/SKR03_DE_GAAP_TOTAL_COST.json index 06e1188..59d1737 100644 --- a/Admin/Install/Coa/SKR03_DE_GAAP_TOTAL_COST.json +++ b/Admin/Install/Coa/SKR03_DE_GAAP_TOTAL_COST.json @@ -99,12 +99,12 @@ { "name": "GPratio", "l11n": { - "en": "Gross profit ratio", - "de": "Rohertragsmarge" + "en": "Gross profit %", + "de": "Rohertragsmarge %" }, "account": [], "type": "formula", - "formula": "GP/(1+2+3+4)", + "formula": "GP/1", "style": "ratio", "children": [] }, @@ -201,11 +201,109 @@ "en": "Other operating expenses", "de": "Sonstige betriebliche Aufwendungen" }, - "account": [2150,2151,2166,2170,2171,2174,2175,2176,2300,2307,2308,2309,2310,2311,2312,2313,2320,2323,2325,2326,2327,2328,2339,2342,2343,2344,2345,2347,2350,2380,2381,2382,2383,2384,2385,2386,2387,2389,2390,2400,2401,2402,2403,2404,2405,2406,2407,2408,2409,2450,2451,2490,2890,2891,2892,2893,2894,2895,2900], + "account": [], "type": "category", "formula": "", + "expanded": true, "style": "category", - "children": [] + "children": [ + { + "name": "8a", + "l11n": { + "en": "General expenses", + "de": "Allgemeine Aufwendungen" + }, + "account": [2010, 2020, 2307, 2309, 4901, 4902, 4903, 4904, 4905, 4909], + "type": "category", + "formula": "", + "style": "category", + "children": [] + }, + { + "name": "8b", + "l11n": { + "en": "Building and property costs", + "de": "Raum- und Grundstückskosten" + }, + "account": [2350, 4200, 4210, 4211, 4212, 4215, 4219, 4220, 4222, 4228, 4229, 4230, 4240, 4250, 4260, 4270, 4280, 4288, 4289, 4290], + "type": "category", + "formula": "", + "style": "category", + "children": [] + }, + { + "name": "8c", + "l11n": { + "en": "Donations", + "de": "Spenden / Zuwendungen" + }, + "account": [2380, 2381, 2382, 2383, 2384, 2387, 2389, 2390], + "type": "category", + "formula": "", + "style": "category", + "children": [] + }, + { + "name": "8d", + "l11n": { + "en": "Levies", + "de": "Versicherungen / Abgaben" + }, + "account": [4360, 4366, 4370, 4380, 4390, 4396, 4397], + "type": "category", + "formula": "", + "style": "category", + "children": [] + }, + { + "name": "8e", + "l11n": { + "en": "Repair / Maintenance", + "de": "Reparatur / Instandhaltung" + }, + "account": [4800, 4801, 4805, 4806, 4809], + "type": "category", + "formula": "", + "style": "category", + "children": [] + }, + { + "name": "8f", + "l11n": { + "en": "Vehicle costs", + "de": "KFZ-Kosten" + }, + "account": [4500, 4520, 4530, 4540, 4550, 4560, 4570, 4575, 4580, 4590], + "type": "category", + "formula": "", + "style": "category", + "children": [] + }, + { + "name": "8g", + "l11n": { + "en": "Advertising and travel expenses", + "de": "Werbe- und Reisekosten" + }, + "account": [4600, 4605, 4630, 4631, 4632, 4635, 4636, 4639, 4640, 4650, 4651, 4652, 4653, 4654, 4655, 4660, 4663, 4664, 4666, 4668, 4670, 4672, 4673, 4674, 4676, 4678, 4679, 4680, 4681], + "type": "category", + "formula": "", + "style": "category", + "children": [] + }, + { + "name": "8h", + "l11n": { + "en": "Other costs", + "de": "Sonstige Kosten" + }, + "account": [2150, 2151, 2166, 2170, 2171, 2176, 2385, 2386, 4139, 4300, 4301, 4306, 4810, 4969, 4970, 4971, 4975, 4976], + "type": "category", + "formula": "", + "style": "category", + "children": [] + } + ] }, { "name": "9", @@ -391,5 +489,17 @@ "formula": "15+16", "style": "total", "children": [] + }, + { + "name": "ProfitRatio", + "l11n": { + "en": "Net profit %", + "de": "Jahresergebnis %" + }, + "account": [], + "type": "formula", + "formula": "(15+16)/1", + "style": "ratio", + "children": [] } ] \ No newline at end of file diff --git a/Admin/Install/db.json b/Admin/Install/db.json index 6ab2ed9..c9525fc 100644 --- a/Admin/Install/db.json +++ b/Admin/Install/db.json @@ -36,11 +36,26 @@ "type": "VARCHAR(255)", "null": false }, + "incomestmt_pl_element_formula": { + "name": "incomestmt_pl_element_formula", + "type": "VARCHAR(255)", + "null": false + }, + "incomestmt_pl_element_style": { + "name": "incomestmt_pl_element_style", + "type": "VARCHAR(255)", + "null": false + }, "incomestmt_pl_element_order": { "name": "incomestmt_pl_element_order", "type": "INT", "null": false }, + "incomestmt_pl_element_expanded": { + "name": "incomestmt_pl_element_expanded", + "type": "TINYINT(1)", + "null": false + }, "incomestmt_pl_element_parent": { "name": "incomestmt_pl_element_parent", "type": "INT", @@ -87,5 +102,36 @@ "foreignKey": "language_639_1" } } + }, + "incomestmt_pl_element_rel": { + "name": "incomestmt_pl_element_rel", + "fields": { + "incomestmt_pl_element_rel_id": { + "name": "incomestmt_pl_element_rel_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "incomestmt_pl_element_rel_order": { + "name": "incomestmt_pl_element_rel_order", + "type": "INT", + "null": true + }, + "incomestmt_pl_element_rel_account": { + "name": "incomestmt_pl_element_rel_account", + "type": "INT", + "null": false, + "foreignTable": "accounting_account", + "foreignKey": "accounting_account_id" + }, + "incomestmt_pl_element_rel_element": { + "name": "incomestmt_pl_element_rel_element", + "type": "INT", + "null": false, + "foreignTable": "incomestmt_pl_element", + "foreignKey": "incomestmt_pl_element_id" + } + } } } \ No newline at end of file diff --git a/Admin/Installer.php b/Admin/Installer.php index b25f483..bbcf398 100644 --- a/Admin/Installer.php +++ b/Admin/Installer.php @@ -14,6 +14,8 @@ declare(strict_types=1); namespace Modules\IncomeStatement\Admin; +use Modules\Accounting\Models\AccountAbstract; +use Modules\Accounting\Models\AccountAbstractMapper; use Modules\IncomeStatement\Controller\ApiController; use phpOMS\Application\ApplicationAbstract; use phpOMS\Config\SettingsInterface; @@ -62,7 +64,7 @@ final class Installer extends InstallerAbstract private static function importStructures(ApplicationAbstract $app) : void { /** @var \Modules\IncomeStatement\Controller\ApiController $module */ - $module = $app->moduleManager->getModuleInstance('IncomeStatement', 'Api'); + $module = $app->moduleManager->get('IncomeStatement', 'Api'); $structures = \scandir(__DIR__ . '/Install/Coa'); foreach ($structures as $file) { @@ -74,8 +76,8 @@ final class Installer extends InstallerAbstract $request = new HttpRequest(); $request->header->account = 1; - $request->setData('code', \strtolower(\basename($file))); - $request->setData('name', \strtr(\basename($file), '_', ' ')); + $request->setData('code', \strtolower(\basename($file, '.json'))); + $request->setData('name', \strtr(\basename($file, '.json'), '_', ' ')); $module->apiIncomeStatementCreate($request, $response); $responseData = $response->getData(''); @@ -102,16 +104,31 @@ final class Installer extends InstallerAbstract $request->setData('code', $element['name']); $request->setData('content', \reset($element['l11n'])); $request->setData('language', \array_keys($element['l11n'])[0] ?? 'en'); - $request->setData('accounts', \implode(',', $element['account'])); $request->setData('formula', $element['formula']); + $request->setData('style', $element['style']); $request->setData('type', $element['type']); $request->setData('pl', $structure); $request->setData('order', $order); + $request->setData('expanded', $element['expanded'] ?? false); if ($parent !== null) { $request->setData('parent', $parent); } + if (!empty($element['account'])) { + $accountObjects = AccountAbstractMapper::getAll() + ->where('code', \array_map(function($account) { + return (string) $account; + }, $element['account']), 'IN') + ->execute(); + + $request->setData('accounts', \implode(',', + \array_map(function (AccountAbstract $account) { + return $account->id; + }, $accountObjects) + )); + } + $module->apiIncomeStatementElementCreate($request, $response); $responseData = $response->getData(''); @@ -130,7 +147,7 @@ final class Installer extends InstallerAbstract $request = new HttpRequest(); $request->header->account = 1; - $request->setData('title', $l11n); + $request->setData('content', $l11n); $request->setData('language', $language); $request->setData('ref', $incomeStatementElement['id']); diff --git a/Controller/ApiController.php b/Controller/ApiController.php index c820712..2fa479d 100644 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -14,6 +14,7 @@ declare(strict_types=1); namespace Modules\IncomeStatement\Controller; +use Modules\Accounting\Models\NullAccountAbstract; use Modules\IncomeStatement\Models\IncomeStatement; use Modules\IncomeStatement\Models\IncomeStatementElement; use Modules\IncomeStatement\Models\IncomeStatementElementL11nMapper; @@ -163,10 +164,18 @@ final class ApiController extends Controller { $element = new IncomeStatementElement(); $element->code = $request->getDataString('code') ?? ''; + $element->formula = $request->getDataString('formula') ?? ''; + $element->style = $request->getDataString('style') ?? ''; $element->incomeStatement = $request->getDataInt('pl') ?? 0; $element->order = $request->getDataInt('order') ?? 0; + $element->expanded = $request->getDataBool('expanded') ?? false; $element->parent = $request->getDataInt('parent'); + $accounts = $request->getDataList('accounts'); + foreach ($accounts as $account) { + $element->accounts[] = new NullAccountAbstract((int) $account); + } + $element->setL11n( $request->getDataString('content') ?? '', ISO639x1Enum::tryFromValue($request->getDataString('language')) ?? ISO639x1Enum::_EN diff --git a/Controller/BackendController.php b/Controller/BackendController.php index 08347ad..11eec1e 100644 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -14,7 +14,9 @@ declare(strict_types=1); namespace Modules\IncomeStatement\Controller; +use Modules\IncomeStatement\Models\IncomeStatementElementL11nMapper; use Modules\IncomeStatement\Models\IncomeStatementElementMapper; +use Modules\IncomeStatement\Models\IncomeStatementMapper; use phpOMS\Contract\RenderableInterface; use phpOMS\DataStorage\Database\Query\OrderType; use phpOMS\Message\RequestAbstract; @@ -49,14 +51,29 @@ final class BackendController extends Controller $view->setTemplate('/Modules/IncomeStatement/Theme/Backend/pl-dashboard'); $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1006401001, $request, $response); - $elements = IncomeStatementElementMapper::getAll() + $view->data['elements'] = IncomeStatementElementMapper::getAll() ->with('l11n') - ->where('incomeStatement', $request->getDataInt('pl') ?? 1) - ->where('l11n/language', $response->header->l11n->language) + ->with('accounts') + ->with('accounts/l11n') + ->where('incomeStatement', $request->getDataInt('structure') ?? 1) + ->where('l11n/language', $request->getDataString('language') ?? $response->header->l11n->language) + ->where('accounts/l11n/language', $request->getDataString('language') ?? $response->header->l11n->language) ->sort('order', OrderType::ASC) ->execute(); - $view->data['elements'] = $elements; + $view->data['structures'] = IncomeStatementMapper::getAll() + ->execute(); + + $view->data['languages'] = []; + if (!empty($view->data['elements'])) { + $tempL11ns = IncomeStatementElementL11nMapper::getAll() + ->where('ref', \reset($view->data['elements'])->id) + ->execute(); + + foreach ($tempL11ns as $l11n) { + $view->data['languages'][] = $l11n->language; + } + } return $view; } diff --git a/Models/IncomeStatementElement.php b/Models/IncomeStatementElement.php index b731437..9b4943b 100644 --- a/Models/IncomeStatementElement.php +++ b/Models/IncomeStatementElement.php @@ -41,12 +41,17 @@ class IncomeStatementElement public string $formula = ''; + public string $style = ''; + public int $order = 0; + public bool $expanded = false; public int $incomeStatement = 0; public ?int $parent = null; + public array $accounts = []; + /* * String l11n * diff --git a/Models/IncomeStatementElementMapper.php b/Models/IncomeStatementElementMapper.php index 36bc21c..66cac5b 100644 --- a/Models/IncomeStatementElementMapper.php +++ b/Models/IncomeStatementElementMapper.php @@ -14,6 +14,7 @@ declare(strict_types=1); namespace Modules\IncomeStatement\Models; +use Modules\Accounting\Models\AccountAbstractMapper; use phpOMS\DataStorage\Database\Mapper\DataMapperFactory; use phpOMS\Localization\BaseStringL11n; @@ -39,7 +40,10 @@ final class IncomeStatementElementMapper extends DataMapperFactory public const COLUMNS = [ 'incomestmt_pl_element_id' => ['name' => 'incomestmt_pl_element_id', 'type' => 'int', 'internal' => 'id'], 'incomestmt_pl_element_code' => ['name' => 'incomestmt_pl_element_code', 'type' => 'string', 'internal' => 'code', 'autocomplete' => true], + 'incomestmt_pl_element_formula' => ['name' => 'incomestmt_pl_element_formula', 'type' => 'string', 'internal' => 'formula', 'autocomplete' => true], + 'incomestmt_pl_element_style' => ['name' => 'incomestmt_pl_element_style', 'type' => 'string', 'internal' => 'style', 'autocomplete' => true], 'incomestmt_pl_element_order' => ['name' => 'incomestmt_pl_element_order', 'type' => 'int', 'internal' => 'order'], + 'incomestmt_pl_element_expanded' => ['name' => 'incomestmt_pl_element_expanded', 'type' => 'bool', 'internal' => 'expanded'], 'incomestmt_pl_element_parent' => ['name' => 'incomestmt_pl_element_parent', 'type' => 'int', 'internal' => 'parent'], 'incomestmt_pl_element_pl' => ['name' => 'incomestmt_pl_element_pl', 'type' => 'int', 'internal' => 'incomeStatement'], ]; @@ -58,6 +62,12 @@ final class IncomeStatementElementMapper extends DataMapperFactory 'column' => 'content', 'external' => null, ], + 'accounts' => [ + 'mapper' => AccountAbstractMapper::class, + 'table' => 'incomestmt_pl_element_rel', + 'self' => 'incomestmt_pl_element_rel_element', + 'external' => 'incomestmt_pl_element_rel_account', + ], ]; /** diff --git a/Theme/Backend/Lang/de.lang.php b/Theme/Backend/Lang/de.lang.php new file mode 100644 index 0000000..123235b --- /dev/null +++ b/Theme/Backend/Lang/de.lang.php @@ -0,0 +1,33 @@ + [ + 'Start' => 'Start', + 'End' => 'Ende', + 'Overview' => 'Übersicht', + 'Metrics' => 'Metriken', + 'Charts' => 'Charts', + 'Interval' => 'Intervall', + 'Environment' => 'Environment', + 'Monthly' => 'Monatlich', + 'Quarterly' => 'Quartalsweise', + 'Annually' => 'Jährlich', + 'Category' => 'Kategorie', + 'Subtotal' => 'Zwischensumme', + 'Total' => 'Summe', + 'HR' => 'Personal', + 'Sales' => 'Umsatz', + 'Diff' => 'Diff', + 'Diff%' => 'Diff %', +]]; diff --git a/Theme/Backend/Lang/en.lang.php b/Theme/Backend/Lang/en.lang.php index 15b21d8..59ac788 100644 --- a/Theme/Backend/Lang/en.lang.php +++ b/Theme/Backend/Lang/en.lang.php @@ -12,5 +12,22 @@ */ declare(strict_types=1); -return ['PL' => [ +return ['IncomeStatement' => [ + 'Start' => 'Start', + 'End' => 'End', + 'Overview' => 'Overview', + 'Metrics' => 'Metrics', + 'Charts' => 'Charts', + 'Interval' => 'Interval', + 'Environment' => 'Environment', + 'Monthly' => 'Monthly', + 'Quarterly' => 'Quarterly', + 'Annually' => 'Annually', + 'Category' => 'Category', + 'Subtotal' => 'Subtotal', + 'Total' => 'Total', + 'HR' => 'HR', + 'Diff' => 'Diff', + 'Sales' => 'Sales', + 'Diff%' => 'Diff %', ]]; diff --git a/Theme/Backend/pl-dashboard.tpl.php b/Theme/Backend/pl-dashboard.tpl.php index f94caa3..9c327c7 100644 --- a/Theme/Backend/pl-dashboard.tpl.php +++ b/Theme/Backend/pl-dashboard.tpl.php @@ -12,54 +12,88 @@ */ declare(strict_types=1); +use phpOMS\Localization\ISO639Enum; + +function render_accounts(array $accounts) : string +{ + $row = ''; + + foreach ($accounts as $account) { + $row .= << +
+
{$account->code} - {$account->getL11n()}
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
1,234.56 %
+
123,456.00
+ + ROW; + } + + return $row; +} + function render_elements(array $elements, ?int $parent = null) : string { $row = ''; $fn = 'render_elements'; + $acc = 'render_accounts'; foreach ($elements as $element) { if ($element->parent !== $parent) { continue; } + $expand = ''; + foreach ($elements as $child) { + if ($child->parent === $element->id + || !empty($element->accounts) + ) { + $expand = ''; + break; + } + } + + $expanded = $element->expanded ? ' checked' : ''; + $row .= << -
-
-
{$element->getL11n()}
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
-
+0.00%
+ +
+
{$expand}
+
{$element->getL11n()}
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
123,456.00
+
1,234.56 %
+
123,456.00
-
{$fn($elements, $element->id)} + {$acc($element->accounts)}
ROW; @@ -70,48 +104,292 @@ function render_elements(array $elements, ?int $parent = null) : string echo $this->data['nav']->render(); ?> + + +
+
+
+
+
+ +
+ +
+ +
-
-
- data['elements'], null); ?> +
+
+
+
+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+ +
+
+
+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+
+ +
+
+ +
+
+ request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>> +
+
+
+
+
getHtml('Category'); ?>
+
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
getHtml('Total'); ?>
+
getHtml('Diff%'); ?>
+
getHtml('Diff'); ?> USD
+
+
+ +
+
data['elements'], null); ?>
+
+
+ + request->uri->fragment === 'c-tab-4' ? ' checked' : ''; ?>> +
+
+
+
+
+ + +
+
+
+
+
+
\ No newline at end of file diff --git a/info.json b/info.json index 35f7f80..01659fb 100644 --- a/info.json +++ b/info.json @@ -16,6 +16,7 @@ }, "directory": "IncomeStatement", "dependencies": { + "Accounting": "*", "Controlling": "*" }, "providing": {