diff --git a/Admin/Install/attributes.json b/Admin/Install/attributes.json index 86228cd..8d756ff 100644 --- a/Admin/Install/attributes.json +++ b/Admin/Install/attributes.json @@ -75,6 +75,50 @@ } } ] + }, + { + "name": "sales_tax_code", + "l11n": { + "en": "Sales tax code", + "de": "USt. code" + }, + "value_type": 2, + "is_custom_allowed": false, + "validation_pattern": "", + "is_required": true, + "default_value": "", + "values": [ + { "value": "AT" }, + { "value": "BE" }, + { "value": "BG" }, + { "value": "HR" }, + { "value": "CY" }, + { "value": "CZ" }, + { "value": "DK" }, + { "value": "EE" }, + { "value": "FI" }, + { "value": "FR" }, + { "value": "DE" }, + { "value": "GR" }, + { "value": "HU" }, + { "value": "IE" }, + { "value": "IT" }, + { "value": "LV" }, + { "value": "LT" }, + { "value": "LU" }, + { "value": "MT" }, + { "value": "NL" }, + { "value": "PL" }, + { "value": "PT" }, + { "value": "RO" }, + { "value": "SK" }, + { "value": "SI" }, + { "value": "ES" }, + { "value": "SE" }, + { "value": "GB" }, + { "value": "INT" }, + { "value": "EU" } + ] }, { "name": "vat_id", diff --git a/Admin/Install/db.json b/Admin/Install/db.json index 8fd410c..4afa06c 100755 --- a/Admin/Install/db.json +++ b/Admin/Install/db.json @@ -40,12 +40,12 @@ "type": "DATETIME", "null": false }, - "clientmgmt_client_profile": { - "name": "clientmgmt_client_profile", + "clientmgmt_client_account": { + "name": "clientmgmt_client_account", "type": "INT", "null": false, - "foreignTable": "profile_account", - "foreignKey": "profile_account_id" + "foreignTable": "account", + "foreignKey": "account_id" }, "clientmgmt_client_address": { "name": "clientmgmt_client_address", @@ -151,6 +151,32 @@ } } }, + "clientmgmt_client_payment": { + "name": "clientmgmt_client_payment", + "fields": { + "clientmgmt_client_payment_id": { + "name": "clientmgmt_client_payment_id", + "type": "INT", + "null": false, + "primary": true, + "autoincrement": true + }, + "clientmgmt_client_payment_dst": { + "name": "clientmgmt_client_payment_dst", + "type": "INT", + "null": false, + "foreignTable": "payment", + "foreignKey": "payment_id" + }, + "clientmgmt_client_payment_src": { + "name": "clientmgmt_client_payment_src", + "type": "INT", + "null": false, + "foreignTable": "clientmgmt_client", + "foreignKey": "clientmgmt_client_id" + } + } + }, "clientmgmt_attr_type": { "name": "clientmgmt_attr_type", "fields": { diff --git a/Admin/Installer.php b/Admin/Installer.php index 17df485..44ed726 100755 --- a/Admin/Installer.php +++ b/Admin/Installer.php @@ -203,8 +203,8 @@ final class Installer extends InstallerAbstract $request->header->account = 1; $request->setData('value', $value['value'] ?? ''); $request->setData('unit', $value['unit'] ?? ''); - $request->setData('default', isset($attribute['values']) && !empty($attribute['values'])); - $request->setData('attributetype', $clientAttrType[$attribute['name']]['id']); + $request->setData('default',true); + $request->setData('type', $clientAttrType[$attribute['name']]['id']); if (isset($value['l11n']) && !empty($value['l11n'])) { $request->setData('title', \reset($value['l11n'])); diff --git a/Controller/ApiController.php b/Controller/ApiController.php index e3d1e72..9817958 100755 --- a/Controller/ApiController.php +++ b/Controller/ApiController.php @@ -16,6 +16,9 @@ namespace Modules\ClientManagement\Controller; use Modules\Admin\Models\Account; use Modules\Admin\Models\Address; +use Modules\Admin\Models\NullAccount; +use Modules\Auditor\Models\Audit; +use Modules\Auditor\Models\AuditMapper; use Modules\ClientManagement\Models\Client; use Modules\ClientManagement\Models\ClientAttribute; use Modules\ClientManagement\Models\ClientAttributeMapper; @@ -35,15 +38,23 @@ use Modules\ClientManagement\Models\NullClientAttributeValue; use Modules\ClientManagement\Models\NullClientL11nType; use Modules\Media\Models\MediaMapper; use Modules\Media\Models\PathSettings; +use Modules\Organization\Models\UnitMapper; use Modules\Profile\Models\ContactElementMapper; use Modules\Profile\Models\Profile; +use phpOMS\Api\EUVAT\EUVATBffOnline; use phpOMS\Localization\BaseStringL11n; +use phpOMS\Localization\ISO3166CharEnum; +use phpOMS\Localization\ISO3166TwoEnum; use phpOMS\Localization\ISO639x1Enum; +use phpOMS\Message\Http\HttpRequest; +use phpOMS\Message\Http\HttpResponse; use phpOMS\Message\Http\RequestStatusCode; use phpOMS\Message\NotificationLevel; use phpOMS\Message\RequestAbstract; use phpOMS\Message\ResponseAbstract; use phpOMS\Model\Message\FormValidation; +use phpOMS\Uri\HttpUri; +use phpOMS\Utils\StringUtils; /** * ClientManagement class. @@ -79,6 +90,78 @@ final class ApiController extends Controller $client = $this->createClientFromRequest($request); $this->createModel($request->header->account, $client, ClientMapper::class, 'client', $request->getOrigin()); + + // Set VAT Id + if ($request->hasData('vat_id')) { + /** @var \Modules\Admin\Models\Unit $unit */ + $unit = UnitMapper::get() + ->with('attributes') + ->execute(); + + $validate = ['status' => -1]; + + if (\in_array($client->mainAddress->getCountry(), ISO3166CharEnum::getRegion('eu'))) { + $validate = EUVATBffOnline::validateQualified( + $unit->getAttribute('vat_id')?->value->getValue() ?? '', + $request->getData('vat_id'), + $client->account->name1, + $client->mainAddress->city, + $client->mainAddress->postal, + $client->mainAddress->address + ); + } + + $audit = new Audit( + new NullAccount($request->header->account), + null, + (string) $validate['status'], + StringUtils::intHash(EUVATBffOnline::class), + 'vat_validation', + self::NAME, + (string) $client->getId(), + \json_encode($validate), + (int) \ip2long($request->getOrigin()) + ); + + AuditMapper::create()->execute($audit); + + if ($validate['status'] === 0) { + /** @var \Modules\ClientManagement\Models\ClientAttributeType $type */ + $type = ClientAttributeTypeMapper::get()->where('name', 'vat_id')->execute(); + + $internalRequest = new HttpRequest(new HttpUri('')); + $internalResponse = new HttpResponse(); + + $internalRequest->header->account = $request->header->account; + $internalRequest->setData('client', $client->getId()); + $internalRequest->setData('type', $type->getId()); + $internalRequest->setData('custom', $request->hasData('vat_id')); + + $this->apiClientAttributeCreate($internalRequest, $internalResponse); + } + } + + // Find tax code + if ($this->app->moduleManager->isActive('Billing')) { + /** @var \Modules\Admin\Models\Unit $unit */ + $unit = UnitMapper::get()->with('mainAddress')->where('id', $this->app->unitId)->execute(); + + /** @var \Modules\ClientManagement\Models\ClientAttributeType $type */ + $type = ClientAttributeTypeMapper::get()->where('name', 'sales_tax_code')->execute(); + + $value = $this->app->moduleManager->get('Billing')->getClientTaxCode($client, $unit->mainAddress); + + $internalRequest = new HttpRequest(new HttpUri('')); + $internalResponse = new HttpResponse(); + + $internalRequest->header->account = $request->header->account; + $internalRequest->setData('client', $client->getId()); + $internalRequest->setData('type', $type->getId()); + $internalRequest->setData('value', $value->getId()); + + $this->apiClientAttributeCreate($internalRequest, $internalResponse); + } + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Client', 'Client successfully created', $client); } @@ -93,21 +176,24 @@ final class ApiController extends Controller */ private function createClientFromRequest(RequestAbstract $request) : Client { - $account = new Account(); - $account->name1 = (string) ($request->getData('name1') ?? ''); - $account->name2 = (string) ($request->getData('name2') ?? ''); - - $profile = new Profile($account); + $account = null; + if (!$request->hasData('account')) { + $account = new Account(); + $account->name1 = (string) ($request->getData('name1') ?? ''); + $account->name2 = (string) ($request->getData('name2') ?? ''); + } else { + $account = new NullAccount((int) $request->getData('account')); + } $client = new Client(); $client->number = (string) ($request->getData('number') ?? ''); - $client->profile = $profile; + $client->account = $account; $addr = new Address(); $addr->address = (string) ($request->getData('address') ?? ''); $addr->postal = (string) ($request->getData('postal') ?? ''); $addr->city = (string) ($request->getData('city') ?? ''); - $addr->setCountry($request->getData('country') ?? ''); + $addr->setCountry($request->getData('country') ?? ISO3166TwoEnum::_XXX); $addr->state = (string) ($request->getData('state') ?? ''); $client->mainAddress = $addr; @@ -129,7 +215,7 @@ final class ApiController extends Controller { $val = []; if (($val['number'] = empty($request->getData('number'))) - || ($val['name1'] = empty($request->getData('name1'))) + || ($val['account'] = empty($request->getData('name1')) && empty($request->getData('account'))) ) { return $val; } @@ -161,7 +247,7 @@ final class ApiController extends Controller $clientL11n = $this->createClientL11nFromRequest($request); $this->createModel($request->header->account, $clientL11n, ClientL11nMapper::class, 'client_l11n', $request->getOrigin()); - $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Client localization', 'Client localization successfully created', $clientL11n); + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Localization', 'Localization successfully created', $clientL11n); } /** @@ -232,7 +318,7 @@ final class ApiController extends Controller $clientL11nType = $this->createClientL11nTypeFromRequest($request); $this->createModel($request->header->account, $clientL11nType, ClientL11nTypeMapper::class, 'client_l11n_type', $request->getOrigin()); - $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Client localization type', 'Client localization type successfully created', $clientL11nType); + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Localization type', 'Localization type successfully created', $clientL11nType); } /** @@ -313,7 +399,17 @@ final class ApiController extends Controller $attribute = new ClientAttribute(); $attribute->client = (int) $request->getData('client'); $attribute->type = new NullClientAttributeType((int) $request->getData('type')); - $attribute->value = new NullClientAttributeValue((int) $request->getData('value')); + + if ($request->getData('value') !== null) { + $attribute->value = new NullClientAttributeValue((int) $request->getData('value')); + } else { + $newRequest = clone $request; + $newRequest->setData('value', $request->getData('custom'), true); + + $value = $this->createClientAttributeValueFromRequest($newRequest); + + $attribute->value = $value; + } return $attribute; } @@ -331,7 +427,7 @@ final class ApiController extends Controller { $val = []; if (($val['type'] = empty($request->getData('type'))) - || ($val['value'] = empty($request->getData('value'))) + || ($val['value'] = (empty($request->getData('value')) && empty($request->getData('custom')))) || ($val['client'] = empty($request->getData('client'))) ) { return $val; @@ -364,7 +460,7 @@ final class ApiController extends Controller $attrL11n = $this->createClientAttributeTypeL11nFromRequest($request); $this->createModel($request->header->account, $attrL11n, ClientAttributeTypeL11nMapper::class, 'attr_type_l11n', $request->getOrigin()); - $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Attribute type localization', 'Attribute type localization successfully created', $attrL11n); + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Localization', 'Localization successfully created', $attrL11n); } /** @@ -589,7 +685,7 @@ final class ApiController extends Controller $attrL11n = $this->createClientAttributeValueL11nFromRequest($request); $this->createModel($request->header->account, $attrL11n, ClientAttributeValueL11nMapper::class, 'attr_value_l11n', $request->getOrigin()); - $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Attribute type localization', 'Attribute type localization successfully created', $attrL11n); + $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Localization', 'Localization successfully created', $attrL11n); } /** diff --git a/Controller/BackendController.php b/Controller/BackendController.php index fb56557..5f90b0e 100755 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -148,8 +148,7 @@ final class BackendController extends Controller /** @var \Modules\ClientManagement\Models\Client $client */ $client = ClientMapper::getAll() - ->with('profile') - ->with('profile/account') + ->with('account') ->with('files') ->with('files/type') ->with('mainAddress') @@ -209,8 +208,7 @@ final class BackendController extends Controller /** @var \Modules\ClientManagement\Models\Client $client */ $client = ClientMapper::get() - ->with('profile') - ->with('profile/account') + ->with('account') ->with('contactElements') ->with('mainAddress') ->with('files')->limit(5, 'files')->sort('files/id', OrderType::DESC) diff --git a/Models/Client.php b/Models/Client.php index e3809f5..08ddbbf 100755 --- a/Models/Client.php +++ b/Models/Client.php @@ -20,6 +20,7 @@ use Modules\Admin\Models\NullAddress; use Modules\Editor\Models\EditorDoc; use Modules\Media\Models\Media; use Modules\Media\Models\NullMedia; +use Modules\Payment\Models\Payment; use Modules\Profile\Models\ContactElement; use Modules\Profile\Models\NullContactElement; use Modules\Profile\Models\Profile; @@ -48,7 +49,7 @@ class Client public \DateTimeImmutable $createdAt; - public Profile $profile; + public Account $account; /** * Attributes. @@ -58,6 +59,14 @@ class Client */ private array $attributes = []; + /** + * Payments. + * + * @var Payment[] + * @since 1.0.0 + */ + private array $payments = []; + /** * Files. * @@ -106,7 +115,7 @@ class Client public function __construct() { $this->createdAt = new \DateTimeImmutable('now'); - $this->profile = new Profile(); + $this->account = new Account(); $this->mainAddress = new NullAddress(); } @@ -278,6 +287,60 @@ class Client return $this->attributes; } + /** + * Get attribute + * + * @param string $attrName Attribute name + * + * @return null|ClientAttribute + * + * @since 1.0.0 + */ + public function getAttribute(string $attrName) : ?ClientAttribute + { + foreach ($this->attributes as $attribute) { + if ($attribute->type->name === $attrName) { + return $attribute->value; + } + } + + return null; + } + + /** + * Get payments + * + * @return Payment[] + * + * @since 1.0.0 + */ + public function getPayments() : array + { + return $this->payments; + } + + /** + * Get payments + * + * @param int $type Payment type + * + * @return array + * + * @since 1.0.0 + */ + public function getPaymentsByType(int $type) : array + { + $payments = []; + + foreach ($this->payments as $payment) { + if ($payment->getType() === $type) { + $payments[] = $payment; + } + } + + return $payments; + } + /** * Get contacts. * diff --git a/Models/ClientAttributeMapper.php b/Models/ClientAttributeMapper.php index 27b9401..0ad25ca 100755 --- a/Models/ClientAttributeMapper.php +++ b/Models/ClientAttributeMapper.php @@ -70,5 +70,5 @@ final class ClientAttributeMapper extends DataMapperFactory * @var string * @since 1.0.0 */ - public const PRIMARYFIELD ='clientmgmt_client_attr_id'; + public const PRIMARYFIELD = 'clientmgmt_client_attr_id'; } diff --git a/Models/ClientAttributeType.php b/Models/ClientAttributeType.php index 53ace1e..74b53f5 100755 --- a/Models/ClientAttributeType.php +++ b/Models/ClientAttributeType.php @@ -110,6 +110,17 @@ class ClientAttributeType implements \JsonSerializable return $this->id; } + public function getDefaultByValue(mixed $value) : ClientAttributeValue + { + foreach ($this->defaults as $default) { + if ($default->getValue() === $value) { + return $default; + } + } + + return new NullClientAttributeValue(); + } + /** * Set l11n * diff --git a/Models/ClientAttributeTypeL11nMapper.php b/Models/ClientAttributeTypeL11nMapper.php index f4ed6df..7293550 100755 --- a/Models/ClientAttributeTypeL11nMapper.php +++ b/Models/ClientAttributeTypeL11nMapper.php @@ -54,7 +54,7 @@ final class ClientAttributeTypeL11nMapper extends DataMapperFactory * @var string * @since 1.0.0 */ - public const PRIMARYFIELD ='clientmgmt_attr_type_l11n_id'; + public const PRIMARYFIELD = 'clientmgmt_attr_type_l11n_id'; /** * Model to use by the mapper. diff --git a/Models/ClientAttributeTypeMapper.php b/Models/ClientAttributeTypeMapper.php index d07a7df..4eff0c9 100755 --- a/Models/ClientAttributeTypeMapper.php +++ b/Models/ClientAttributeTypeMapper.php @@ -78,5 +78,5 @@ final class ClientAttributeTypeMapper extends DataMapperFactory * @var string * @since 1.0.0 */ - public const PRIMARYFIELD ='clientmgmt_attr_type_id'; + public const PRIMARYFIELD = 'clientmgmt_attr_type_id'; } diff --git a/Models/ClientAttributeValueL11nMapper.php b/Models/ClientAttributeValueL11nMapper.php index d720a24..6f7e622 100644 --- a/Models/ClientAttributeValueL11nMapper.php +++ b/Models/ClientAttributeValueL11nMapper.php @@ -54,7 +54,7 @@ final class ClientAttributeValueL11nMapper extends DataMapperFactory * @var string * @since 1.0.0 */ - public const PRIMARYFIELD ='clientmgmt_attr_value_l11n_id'; + public const PRIMARYFIELD = 'clientmgmt_attr_value_l11n_id'; /** * Model to use by the mapper. diff --git a/Models/ClientAttributeValueMapper.php b/Models/ClientAttributeValueMapper.php index 3f3f74b..b14cd10 100755 --- a/Models/ClientAttributeValueMapper.php +++ b/Models/ClientAttributeValueMapper.php @@ -73,5 +73,5 @@ final class ClientAttributeValueMapper extends DataMapperFactory * @var string * @since 1.0.0 */ - public const PRIMARYFIELD ='clientmgmt_attr_value_id'; + public const PRIMARYFIELD = 'clientmgmt_attr_value_id'; } diff --git a/Models/ClientL11nMapper.php b/Models/ClientL11nMapper.php index 1888a9d..b18a75a 100755 --- a/Models/ClientL11nMapper.php +++ b/Models/ClientL11nMapper.php @@ -67,5 +67,5 @@ final class ClientL11nMapper extends DataMapperFactory * @var string * @since 1.0.0 */ - public const PRIMARYFIELD ='clientmgmt_client_l11n_id'; + public const PRIMARYFIELD = 'clientmgmt_client_l11n_id'; } diff --git a/Models/ClientL11nTypeMapper.php b/Models/ClientL11nTypeMapper.php index 2cc5446..35fc60f 100755 --- a/Models/ClientL11nTypeMapper.php +++ b/Models/ClientL11nTypeMapper.php @@ -52,5 +52,5 @@ final class ClientL11nTypeMapper extends DataMapperFactory * @var string * @since 1.0.0 */ - public const PRIMARYFIELD ='clientmgmt_client_l11n_type_id'; + public const PRIMARYFIELD = 'clientmgmt_client_l11n_type_id'; } diff --git a/Models/ClientMapper.php b/Models/ClientMapper.php index ae6e0f9..e734836 100755 --- a/Models/ClientMapper.php +++ b/Models/ClientMapper.php @@ -14,9 +14,11 @@ declare(strict_types=1); namespace Modules\ClientManagement\Models; +use Modules\Admin\Models\AccountMapper; use Modules\Admin\Models\AddressMapper; use Modules\Editor\Models\EditorDocMapper; use Modules\Media\Models\MediaMapper; +use Modules\Payment\Models\PaymentMapper; use Modules\Profile\Models\ContactElementMapper; use Modules\Profile\Models\ProfileMapper; use phpOMS\DataStorage\Database\Mapper\DataMapperFactory; @@ -45,7 +47,7 @@ final class ClientMapper extends DataMapperFactory 'clientmgmt_client_type' => ['name' => 'clientmgmt_client_type', 'type' => 'int', 'internal' => 'type'], 'clientmgmt_client_info' => ['name' => 'clientmgmt_client_info', 'type' => 'string', 'internal' => 'info'], 'clientmgmt_client_created_at' => ['name' => 'clientmgmt_client_created_at', 'type' => 'DateTimeImmutable', 'internal' => 'createdAt', 'readonly' => true], - 'clientmgmt_client_profile' => ['name' => 'clientmgmt_client_profile', 'type' => 'int', 'internal' => 'profile'], + 'clientmgmt_client_account' => ['name' => 'clientmgmt_client_account', 'type' => 'int', 'internal' => 'account'], 'clientmgmt_client_address' => ['name' => 'clientmgmt_client_address', 'type' => 'int', 'internal' => 'mainAddress'], 'clientmgmt_client_unit' => ['name' => 'clientmgmt_client_unit', 'type' => 'int', 'internal' => 'unit'], ]; @@ -64,7 +66,7 @@ final class ClientMapper extends DataMapperFactory * @var string * @since 1.0.0 */ - public const PRIMARYFIELD ='clientmgmt_client_id'; + public const PRIMARYFIELD = 'clientmgmt_client_id'; /** * Created at column @@ -81,9 +83,9 @@ final class ClientMapper extends DataMapperFactory * @since 1.0.0 */ public const OWNS_ONE = [ - 'profile' => [ - 'mapper' => ProfileMapper::class, - 'external' => 'clientmgmt_client_profile', + 'account' => [ + 'mapper' => AccountMapper::class, + 'external' => 'clientmgmt_client_account', ], 'mainAddress' => [ 'mapper' => AddressMapper::class, @@ -116,6 +118,12 @@ final class ClientMapper extends DataMapperFactory 'external' => 'clientmgmt_client_contactelement_dst', 'self' => 'clientmgmt_client_contactelement_src', ], + 'payments' => [ + 'mapper' => PaymentMapper::class, + 'table' => 'clientmgmt_client_payment', + 'external' => 'clientmgmt_client_payment_dst', + 'self' => 'clientmgmt_client_payment_src', + ], 'attributes' => [ 'mapper' => ClientAttributeMapper::class, 'table' => 'clientmgmt_client_attr', diff --git a/Theme/Backend/client-profile.tpl.php b/Theme/Backend/client-profile.tpl.php index 7356d4e..0c7b858 100755 --- a/Theme/Backend/client-profile.tpl.php +++ b/Theme/Backend/client-profile.tpl.php @@ -65,11 +65,11 @@ echo $this->getData('nav')->render();