diff --git a/Admin/Install/Admin.install.json b/Admin/Install/Admin.install.json
new file mode 100644
index 0000000..cbe32b1
--- /dev/null
+++ b/Admin/Install/Admin.install.json
@@ -0,0 +1,10 @@
+[
+ {
+ "description": "Default client segmentation (segment, section, sales group, product group)",
+ "type": "setting",
+ "name": "1003100001",
+ "content": "[\"segment\":1, \"section\":1, \"client_group\":1]",
+ "pattern": "",
+ "module": "ClientManagement"
+ }
+]
\ No newline at end of file
diff --git a/Admin/Install/Admin.php b/Admin/Install/Admin.php
new file mode 100644
index 0000000..3332c9a
--- /dev/null
+++ b/Admin/Install/Admin.php
@@ -0,0 +1,43 @@
+ __DIR__ . '/Admin.install.json']);
+ }
+}
diff --git a/Admin/Routes/Web/Api.php b/Admin/Routes/Web/Api.php
new file mode 100644
index 0000000..f927940
--- /dev/null
+++ b/Admin/Routes/Web/Api.php
@@ -0,0 +1,172 @@
+ [
+ [
+ 'dest' => '\Modules\ClientManagement\Controller\ApiController:apiClientFind',
+ 'verb' => RouteVerb::GET,
+ 'permission' => [
+ 'module' => ApiController::NAME,
+ 'type' => PermissionType::READ,
+ 'state' => PermissionCategory::CLIENT,
+ ],
+ ],
+ ],
+ '^.*/client/attribute$' => [
+ [
+ 'dest' => '\Modules\ClientManagement\Controller\ApiAttributeController:apiClientAttributeCreate',
+ 'verb' => RouteVerb::PUT,
+ 'permission' => [
+ 'module' => ApiController::NAME,
+ 'type' => PermissionType::READ,
+ 'state' => PermissionCategory::CLIENT,
+ ],
+ ],
+ [
+ 'dest' => '\Modules\ClientManagement\Controller\ApiAttributeController:apiClientAttributeUpdate',
+ 'verb' => RouteVerb::SET,
+ 'permission' => [
+ 'module' => ApiController::NAME,
+ 'type' => PermissionType::READ,
+ 'state' => PermissionCategory::CLIENT,
+ ],
+ ],
+ ],
+ '^.*/client/attribute/type$' => [
+ [
+ 'dest' => '\Modules\ClientManagement\Controller\ApiAttributeController:apiClientAttributeTypeCreate',
+ 'verb' => RouteVerb::PUT,
+ 'permission' => [
+ 'module' => ApiController::NAME,
+ 'type' => PermissionType::READ,
+ 'state' => PermissionCategory::CLIENT,
+ ],
+ ],
+ [
+ 'dest' => '\Modules\ClientManagement\Controller\ApiAttributeController:apiClientAttributeTypeUpdate',
+ 'verb' => RouteVerb::SET,
+ 'permission' => [
+ 'module' => ApiController::NAME,
+ 'type' => PermissionType::READ,
+ 'state' => PermissionCategory::CLIENT,
+ ],
+ ],
+ ],
+ '^.*/client/attribute/type/l11n$' => [
+ [
+ 'dest' => '\Modules\ClientManagement\Controller\ApiAttributeController:apiClientAttributeTypeL11nCreate',
+ 'verb' => RouteVerb::PUT,
+ 'permission' => [
+ 'module' => ApiController::NAME,
+ 'type' => PermissionType::READ,
+ 'state' => PermissionCategory::CLIENT,
+ ],
+ ],
+ [
+ 'dest' => '\Modules\ClientManagement\Controller\ApiAttributeController:apiClientAttributeTypeL11nUpdate',
+ 'verb' => RouteVerb::SET,
+ 'permission' => [
+ 'module' => ApiController::NAME,
+ 'type' => PermissionType::READ,
+ 'state' => PermissionCategory::CLIENT,
+ ],
+ ],
+ ],
+ '^.*/client/attribute/value$' => [
+ [
+ 'dest' => '\Modules\ClientManagement\Controller\ApiAttributeController:apiClientAttributeValueCreate',
+ 'verb' => RouteVerb::PUT,
+ 'permission' => [
+ 'module' => ApiController::NAME,
+ 'type' => PermissionType::READ,
+ 'state' => PermissionCategory::CLIENT,
+ ],
+ ],
+ [
+ 'dest' => '\Modules\ClientManagement\Controller\ApiAttributeController:apiClientAttributeValueUpdate',
+ 'verb' => RouteVerb::SET,
+ 'permission' => [
+ 'module' => ApiController::NAME,
+ 'type' => PermissionType::READ,
+ 'state' => PermissionCategory::CLIENT,
+ ],
+ ],
+ ],
+ '^.*/client/attribute/value$' => [
+ [
+ 'dest' => '\Modules\ClientManagement\Controller\ApiAttributeController:apiClientAttributeValueL11nCreate',
+ 'verb' => RouteVerb::PUT,
+ 'permission' => [
+ 'module' => ApiController::NAME,
+ 'type' => PermissionType::READ,
+ 'state' => PermissionCategory::CLIENT,
+ ],
+ ],
+ [
+ 'dest' => '\Modules\ClientManagement\Controller\ApiAttributeController:apiClientAttributeValueL11nUpdate',
+ 'verb' => RouteVerb::SET,
+ 'permission' => [
+ 'module' => ApiController::NAME,
+ 'type' => PermissionType::READ,
+ 'state' => PermissionCategory::CLIENT,
+ ],
+ ],
+ ],
+ '^.*/client/l11n$' => [
+ [
+ 'dest' => '\Modules\ClientManagement\Controller\ApiController:apiClientL11nCreate',
+ 'verb' => RouteVerb::PUT,
+ 'permission' => [
+ 'module' => ApiController::NAME,
+ 'type' => PermissionType::READ,
+ 'state' => PermissionCategory::CLIENT,
+ ],
+ ],
+ [
+ 'dest' => '\Modules\ClientManagement\Controller\ApiController:apiClientL11nUpdate',
+ 'verb' => RouteVerb::SET,
+ 'permission' => [
+ 'module' => ApiController::NAME,
+ 'type' => PermissionType::READ,
+ 'state' => PermissionCategory::CLIENT,
+ ],
+ ],
+ ],
+ '^.*/client/l11n/type$' => [
+ [
+ 'dest' => '\Modules\ClientManagement\Controller\ApiController:apiClientL11nTypeCreate',
+ 'verb' => RouteVerb::PUT,
+ 'permission' => [
+ 'module' => ApiController::NAME,
+ 'type' => PermissionType::READ,
+ 'state' => PermissionCategory::CLIENT,
+ ],
+ ],
+ [
+ 'dest' => '\Modules\ClientManagement\Controller\ApiController:apiClientL11nTypeUpdate',
+ 'verb' => RouteVerb::SET,
+ 'permission' => [
+ 'module' => ApiController::NAME,
+ 'type' => PermissionType::READ,
+ 'state' => PermissionCategory::CLIENT,
+ ],
+ ],
+ ],
+];
diff --git a/Controller/ApiController.php b/Controller/ApiController.php
index bffbbe5..85463dd 100755
--- a/Controller/ApiController.php
+++ b/Controller/ApiController.php
@@ -25,6 +25,7 @@ use Modules\ClientManagement\Models\ClientL11nMapper;
use Modules\ClientManagement\Models\ClientL11nTypeMapper;
use Modules\ClientManagement\Models\ClientMapper;
use Modules\ClientManagement\Models\PermissionCategory;
+use Modules\ClientManagement\Models\SettingsEnum;
use Modules\Media\Models\MediaMapper;
use Modules\Media\Models\PathSettings;
use Modules\Organization\Models\UnitMapper;
@@ -193,9 +194,40 @@ final class ApiController extends Controller
->apiClientAttributeCreate($internalRequest, $internalResponse);
}
+ $this->createClientSegmentation($request, $response, $client);
+
$this->createStandardCreateResponse($request, $response, $client);
}
+ private function createClientSegmentation(RequestAbstract $request, ResponseAbstract $response, Client $client) : void
+ {
+ /** @var \Model\Setting $settings */
+ $settings = $this->app->appSettings->get(null, [
+ SettingsEnum::DEFAULT_SEGMENTATION,
+ ]);
+
+ $segmentation = \json_decode($settings->content, true);
+ if ($segmentation === false) {
+ return;
+ }
+
+ $types = ClientAttributeTypeMapper::get()
+ ->where('name', \array_keys($segmentation), 'IN')
+ ->execute();
+
+ foreach ($types as $type) {
+ $internalResponse = clone $response;
+ $internalRequest = new HttpRequest(new HttpUri(''));
+
+ $internalRequest->header->account = $request->header->account;
+ $internalRequest->setData('ref', $client->id);
+ $internalRequest->setData('type', $type->id);
+ $internalRequest->setData('value_id', $segmentation[$type->name]);
+
+ $this->app->moduleManager->get('ClientManagement', 'ApiAttribute')->apiItemAttributeCreate($internalRequest, $internalResponse);
+ }
+ }
+
/**
* Method to create news article from request.
*
@@ -219,25 +251,25 @@ final class ApiController extends Controller
$client = new Client();
$client->number = $request->getDataString('number') ?? '';
$client->account = $account;
+ $client->unit = $request->getDataInt('unit') ?? 1;
+ // Handle main address
$addr = new Address();
$addr->address = $request->getDataString('address') ?? '';
$addr->postal = $request->getDataString('postal') ?? '';
$addr->city = $request->getDataString('city') ?? '';
- $addr->setCountry($request->getDataString('country') ?? ISO3166TwoEnum::_XXX);
$addr->state = $request->getDataString('state') ?? '';
+ $addr->setCountry($request->getDataString('country') ?? ISO3166TwoEnum::_XXX);
+ $client->mainAddress = $addr;
+ // Try to find lat/lon through external API
$geocoding = Nominatim::geocoding($addr->country, $addr->city, $addr->address);
if ($geocoding === ['lat' => 0.0, 'lon' => 0.0]) {
$geocoding = Nominatim::geocoding($addr->country, $addr->city);
}
- $addr->lat = $geocoding['lat'];
- $addr->lon = $geocoding['lon'];
-
- $client->mainAddress = $addr;
-
- $client->unit = $request->getDataInt('unit');
+ $client->mainAddress->lat = $geocoding['lat'];
+ $client->mainAddress->lon = $geocoding['lon'];
return $client;
}
@@ -340,13 +372,13 @@ final class ApiController extends Controller
*/
private function updateMainAddressFromRequest(RequestAbstract $request, Address $address) : Address
{
- $address->name = $request->getDataString('name') ?? $address->name;
- $address->fao = $request->getDataString('fao') ?? $address->fao;
- $address->address = $request->getDataString('address') ?? $address->address;
+ $address->name = $request->getDataString('name') ?? $address->name;
+ $address->fao = $request->getDataString('fao') ?? $address->fao;
+ $address->address = $request->getDataString('address') ?? $address->address;
$address->addressAddition = $request->getDataString('addition') ?? $address->addressAddition;
- $address->postal = $request->getDataString('postal') ?? $address->postal;
- $address->city = $request->getDataString('city') ?? $address->city;
- $address->state = $request->getDataString('state') ?? $address->state;
+ $address->postal = $request->getDataString('postal') ?? $address->postal;
+ $address->city = $request->getDataString('city') ?? $address->city;
+ $address->state = $request->getDataString('state') ?? $address->state;
$address->setCountry($request->getDataString('country') ?? $address->getCountry());
return $address;
diff --git a/Controller/BackendController.php b/Controller/BackendController.php
index 51f258d..d584c18 100755
--- a/Controller/BackendController.php
+++ b/Controller/BackendController.php
@@ -14,23 +14,29 @@ declare(strict_types=1);
namespace Modules\ClientManagement\Controller;
+use Modules\Admin\Models\LocalizationMapper;
+use Modules\Admin\Models\SettingsEnum;
+use Modules\Auditor\Models\AuditMapper;
+use Modules\Billing\Models\Price\PriceMapper;
+use Modules\Billing\Models\Price\PriceType;
use Modules\Billing\Models\SalesBillMapper;
+use Modules\ClientManagement\Models\Attribute\ClientAttributeMapper;
use Modules\ClientManagement\Models\Attribute\ClientAttributeTypeL11nMapper;
use Modules\ClientManagement\Models\Attribute\ClientAttributeTypeMapper;
use Modules\ClientManagement\Models\Attribute\ClientAttributeValueMapper;
use Modules\ClientManagement\Models\ClientMapper;
use Modules\Media\Models\MediaMapper;
use Modules\Media\Models\MediaTypeMapper;
+use Modules\Organization\Models\Attribute\UnitAttributeMapper;
use phpOMS\Asset\AssetType;
use phpOMS\Contract\RenderableInterface;
use phpOMS\DataStorage\Database\Query\Builder;
use phpOMS\DataStorage\Database\Query\OrderType;
-use phpOMS\Localization\ISO3166CharEnum;
-use phpOMS\Localization\ISO3166NameEnum;
use phpOMS\Localization\Money;
use phpOMS\Message\RequestAbstract;
use phpOMS\Message\ResponseAbstract;
use phpOMS\Stdlib\Base\SmartDateTime;
+use phpOMS\Utils\StringUtils;
use phpOMS\Views\View;
/**
@@ -45,7 +51,7 @@ use phpOMS\Views\View;
final class BackendController extends Controller
{
/**
- * Routing end-point for application behaviour.
+ * Routing end-point for application behavior.
*
* @param RequestAbstract $request Request
* @param ResponseAbstract $response Response
@@ -74,7 +80,7 @@ final class BackendController extends Controller
}
/**
- * Routing end-point for application behaviour.
+ * Routing end-point for application behavior.
*
* @param RequestAbstract $request Request
* @param ResponseAbstract $response Response
@@ -103,7 +109,7 @@ final class BackendController extends Controller
}
/**
- * Routing end-point for application behaviour.
+ * Routing end-point for application behavior.
*
* @param RequestAbstract $request Request
* @param ResponseAbstract $response Response
@@ -138,7 +144,7 @@ final class BackendController extends Controller
}
/**
- * Routing end-point for application behaviour.
+ * Routing end-point for application behavior.
*
* @param RequestAbstract $request Request
* @param ResponseAbstract $response Response
@@ -168,7 +174,7 @@ final class BackendController extends Controller
}
/**
- * Routing end-point for application behaviour.
+ * Routing end-point for application behavior.
*
* @param RequestAbstract $request Request
* @param ResponseAbstract $response Response
@@ -189,7 +195,7 @@ final class BackendController extends Controller
}
/**
- * Routing end-point for application behaviour.
+ * Routing end-point for application behavior.
*
* @param RequestAbstract $request Request
* @param ResponseAbstract $response Response
@@ -221,11 +227,32 @@ final class BackendController extends Controller
->with('mainAddress')
->with('files')->limit(5, 'files')->sort('files/id', OrderType::DESC)
->with('notes')->limit(5, 'notes')->sort('notes/id', OrderType::DESC)
+ ->with('attributes')
+ ->with('attributes/type')
+ ->with('attributes/type/l11n')
+ ->with('attributes/value')
->where('id', (int) $request->getData('id'))
->execute();
$view->data['client'] = $client;
+ /** @var \Model\Setting $settings */
+ $settings = $this->app->appSettings->get(null, [
+ SettingsEnum::DEFAULT_LOCALIZATION,
+ ]);
+
+
+ $view->data['attributeView'] = new \Modules\Attribute\Theme\Backend\Components\AttributeView($this->app->l11nManager, $request, $response);
+ $view->data['attributeView']->data['defaultlocalization'] = LocalizationMapper::get()->where('id', (int) $settings->id)->execute();
+
+ /** @var \Modules\Attribute\Models\AttributeType[] $attributeTypes */
+ $attributeTypes = ClientAttributeTypeMapper::getAll()
+ ->with('l11n')
+ ->where('l11n/language', $response->header->l11n->language)
+ ->execute();
+
+ $view->data['attributeTypes'] = $attributeTypes;
+
// Get item profile image
// It might not be part of the 5 newest item files from above
// @todo It would be nice to have something like this as a default method in the model e.g.
@@ -253,44 +280,53 @@ final class BackendController extends Controller
$view->data['clientImage'] = $clientImage;
- // stats
- if ($this->app->moduleManager->isActive('Billing')) {
- $ytd = SalesBillMapper::getSalesByClientId($client->id, new SmartDateTime('Y-01-01'), new SmartDateTime('now'));
- $mtd = SalesBillMapper::getSalesByClientId($client->id, new SmartDateTime('Y-m-01'), new SmartDateTime('now'));
- $lastOrder = SalesBillMapper::getLastOrderDateByClientId($client->id);
- $newestInvoices = SalesBillMapper::getAll()
- ->with('type')
- ->with('type/l11n')
- ->with('client')
- ->where('client', $client->id)
- ->where('type/l11n/language', $response->header->l11n->language)
- ->sort('id', OrderType::DESC)
- ->limit(5)
- ->execute();
+ $businessStart = UnitAttributeMapper::get()
+ ->with('type')
+ ->with('value')
+ ->where('ref', $this->app->unitId)
+ ->where('type/name', 'business_year_start')
+ ->execute();
- $monthlySalesCosts = SalesBillMapper::getClientMonthlySalesCosts($client->id, (new SmartDateTime('now'))->createModify(-1), new SmartDateTime('now'));
- $items = SalesBillMapper::getClientItem($client->id, (new SmartDateTime('now'))->createModify(-1), new SmartDateTime('now'));
- } else {
- $ytd = new Money();
- $mtd = new Money();
- $lastOrder = null;
- $newestInvoices = [];
- $monthlySalesCosts = [];
- $items = [];
- }
+ $view->data['business_start'] = $businessStart->id === 0 ? 1 : $businessStart->value->getValue();
- $view->data['ytd'] = $ytd;
- $view->data['mtd'] = $mtd;
- $view->data['lastOrder'] = $lastOrder;
- $view->data['newestInvoices'] = $newestInvoices;
- $view->data['monthlySalesCosts'] = $monthlySalesCosts;
- $view->data['items'] = $items;
+ /** @var \Modules\Billing\Models\Price\Price[] $prices */
+ $prices = PriceMapper::getAll()
+ ->where('client', $client->id)
+ ->where('type', PriceType::SALES)
+ ->execute();
+
+ $view->data['prices'] = $prices;
+
+ /** @var \Modules\Auditor\Models\Audit[] $audits */
+ $audits = AuditMapper::getAll()
+ ->where('type', StringUtils::intHash(ClientMapper::class))
+ ->where('module', 'ClientManagement')
+ ->where('ref', (string) $client->id)
+ ->execute();
+
+ // @todo join audit with files, attributes, localization, prices, notes, ...
+
+ $view->data['audits'] = $audits;
+
+ /** @var \Modules\Media\Models\Media[] $files */
+ $files = MediaMapper::getAll()
+ ->with('types')
+ ->join('id', ClientMapper::class, 'files') // id = media id, files = client relations
+ ->on('id', $client->id, relation: 'files') // id = item id
+ ->execute();
+
+ $view->data['files'] = $files;
+
+ $view->data['media-upload'] = new \Modules\Media\Theme\Backend\Components\Upload\BaseView($this->app->l11nManager, $request, $response);
+ $view->data['note'] = new \Modules\Editor\Theme\Backend\Components\Note\BaseView($this->app->l11nManager, $request, $response);
+
+ $view->data['hasBilling'] = $this->app->moduleManager->isActive('Billing');
return $view;
}
/**
- * Routing end-point for application behaviour.
+ * Routing end-point for application behavior.
*
* @param RequestAbstract $request Request
* @param ResponseAbstract $response Response
diff --git a/Models/ClientMapper.php b/Models/ClientMapper.php
index 3e61117..eb3629e 100755
--- a/Models/ClientMapper.php
+++ b/Models/ClientMapper.php
@@ -22,6 +22,8 @@ use Modules\Media\Models\MediaMapper;
use Modules\Payment\Models\PaymentMapper;
use Modules\Profile\Models\ContactElementMapper;
use phpOMS\DataStorage\Database\Mapper\DataMapperFactory;
+use phpOMS\DataStorage\Database\Query\Builder;
+use phpOMS\Stdlib\Base\FloatInt;
/**
* Client mapper class.
diff --git a/Models/PermissionCategory.php b/Models/PermissionCategory.php
index b3a0d4e..f8b754e 100755
--- a/Models/PermissionCategory.php
+++ b/Models/PermissionCategory.php
@@ -17,7 +17,7 @@ namespace Modules\ClientManagement\Models;
use phpOMS\Stdlib\Base\Enum;
/**
- * Permision state enum.
+ * Permission category enum.
*
* @package Modules\ClientManagement\Models
* @license OMS License 2.0
diff --git a/Models/SettingsEnum.php b/Models/SettingsEnum.php
new file mode 100644
index 0000000..e29ab90
--- /dev/null
+++ b/Models/SettingsEnum.php
@@ -0,0 +1,30 @@
+data['nav']->render(); ?>
= $this->getHtml('Language', '0', '0'); ?>download
-
+
|
diff --git a/Theme/Backend/client-create.tpl.php b/Theme/Backend/client-create.tpl.php
index 611231b..53346d6 100755
--- a/Theme/Backend/client-create.tpl.php
+++ b/Theme/Backend/client-create.tpl.php
@@ -20,14 +20,14 @@ echo $this->data['nav']->render(); ?>
|
@@ -264,7 +264,7 @@ echo $this->data['nav']->render(); ?>
$footerView->setPage(1);
?>
-
+
= $this->getHtml('Logs'); ?>download
diff --git a/Theme/Backend/client-profile-bills.tpl.php b/Theme/Backend/client-profile-bills.tpl.php
deleted file mode 100755
index c22cdb4..0000000
--- a/Theme/Backend/client-profile-bills.tpl.php
+++ /dev/null
@@ -1,194 +0,0 @@
-data['newestInvoices'] ?? [];
-
-?>
-
-
-
-
-
= $this->getHtml('Bills'); ?>download
-
-
-
-
diff --git a/Theme/Backend/client-profile-items.tpl.php b/Theme/Backend/client-profile-items.tpl.php
index 0cb6a9c..27e9fd8 100755
--- a/Theme/Backend/client-profile-items.tpl.php
+++ b/Theme/Backend/client-profile-items.tpl.php
@@ -23,7 +23,7 @@ $items = $this->data['items'] ?? [];
= $this->getHtml('Items'); ?>download
-
+
|