Fix e-conomic EAN customer transfer
This commit is contained in:
@@ -137,6 +137,10 @@ class customer_mass_import_service
|
|||||||
if ($cvrLength < 8 || $cvrLength > 20) {
|
if ($cvrLength < 8 || $cvrLength > 20) {
|
||||||
throw new \RuntimeException('CVR must be between 8 and 20 digits.', 400);
|
throw new \RuntimeException('CVR must be between 8 and 20 digits.', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($normalized['ean'] !== null && strlen((string)$normalized['ean']) > 13) {
|
||||||
|
throw new \RuntimeException('EAN must be at most 13 digits.', 400);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function normalizePositiveInt(mixed $value): ?int
|
protected function normalizePositiveInt(mixed $value): ?int
|
||||||
|
|||||||
@@ -172,7 +172,8 @@ class economic implements economic_i
|
|||||||
string $email,
|
string $email,
|
||||||
int $phone,
|
int $phone,
|
||||||
?int $mobile_phone = null,
|
?int $mobile_phone = null,
|
||||||
object|array|null $company_information = null
|
object|array|null $company_information = null,
|
||||||
|
?string $ean = null
|
||||||
): object
|
): object
|
||||||
{
|
{
|
||||||
$payload = [
|
$payload = [
|
||||||
@@ -196,10 +197,37 @@ class economic implements economic_i
|
|||||||
];
|
];
|
||||||
|
|
||||||
$payload = array_replace($payload, $this->buildCustomerPayloadFromCompanyInformation($company_information));
|
$payload = array_replace($payload, $this->buildCustomerPayloadFromCompanyInformation($company_information));
|
||||||
|
$normalized_ean = self::normalizeCustomerEan($ean);
|
||||||
|
if ($normalized_ean !== null) {
|
||||||
|
$payload['ean'] = $normalized_ean;
|
||||||
|
}
|
||||||
|
|
||||||
return $this->customers->customers->create($payload);
|
return $this->customers->customers->create($payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function normalizeCustomerEan(mixed $value): ?string
|
||||||
|
{
|
||||||
|
if ($value === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$digits = preg_replace('/\D+/', '', (string)$value);
|
||||||
|
if (!is_string($digits)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$digits = trim($digits);
|
||||||
|
if ($digits === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($digits) > 13) {
|
||||||
|
throw new \InvalidArgumentException('EAN must be at most 13 digits.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $digits;
|
||||||
|
}
|
||||||
|
|
||||||
private function buildCustomerPayloadFromCompanyInformation(object|array|null $company_information): array
|
private function buildCustomerPayloadFromCompanyInformation(object|array|null $company_information): array
|
||||||
{
|
{
|
||||||
if ($company_information === null) {
|
if ($company_information === null) {
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ class economic_customer_mo
|
|||||||
public null|string $message;
|
public null|string $message;
|
||||||
public null|string $corporateIdentificationNumber;
|
public null|string $corporateIdentificationNumber;
|
||||||
public null|string $email;
|
public null|string $email;
|
||||||
|
public null|string $ean;
|
||||||
|
public null|string $publicEntryNumber;
|
||||||
public null|string $mobilePhone;
|
public null|string $mobilePhone;
|
||||||
public null|string $currency;
|
public null|string $currency;
|
||||||
public null|string $country;
|
public null|string $country;
|
||||||
@@ -46,6 +48,8 @@ class economic_customer_mo
|
|||||||
$this->zip = ($customer->zip ?? null);
|
$this->zip = ($customer->zip ?? null);
|
||||||
$this->corporateIdentificationNumber = ($customer->corporateIdentificationNumber ?? null);
|
$this->corporateIdentificationNumber = ($customer->corporateIdentificationNumber ?? null);
|
||||||
$this->email = ($customer->email ?? null);
|
$this->email = ($customer->email ?? null);
|
||||||
|
$this->ean = ($customer->ean ?? null);
|
||||||
|
$this->publicEntryNumber = ($customer->publicEntryNumber ?? $customer->public_entry_number ?? null);
|
||||||
$this->mobilePhone = ($customer->mobilePhone ?? null);
|
$this->mobilePhone = ($customer->mobilePhone ?? null);
|
||||||
$this->currency = ($customer->currency ?? null);
|
$this->currency = ($customer->currency ?? null);
|
||||||
$this->country = ($customer->country ?? null);
|
$this->country = ($customer->country ?? null);
|
||||||
@@ -100,6 +104,8 @@ class economic_customer_mo
|
|||||||
'zip' => $this->zip,
|
'zip' => $this->zip,
|
||||||
'corporateIdentificationNumber' => $this->corporateIdentificationNumber,
|
'corporateIdentificationNumber' => $this->corporateIdentificationNumber,
|
||||||
'email' => $this->email,
|
'email' => $this->email,
|
||||||
|
'ean' => $this->ean,
|
||||||
|
'publicEntryNumber' => $this->publicEntryNumber,
|
||||||
'mobilePhone' => $this->mobilePhone,
|
'mobilePhone' => $this->mobilePhone,
|
||||||
'currency' => $this->currency,
|
'currency' => $this->currency,
|
||||||
'country' => $this->country,
|
'country' => $this->country,
|
||||||
|
|||||||
+19
-10
@@ -127,6 +127,23 @@ class economic_invoices_drafts_endpoint
|
|||||||
$customer_address = $customer->getAddress() ?? 'Ukendt';
|
$customer_address = $customer->getAddress() ?? 'Ukendt';
|
||||||
$customer_zip = $customer->getZipCode() ?? 'Ukendt';
|
$customer_zip = $customer->getZipCode() ?? 'Ukendt';
|
||||||
$customer_city = $customer->getCity() ?? 'Ukendt';
|
$customer_city = $customer->getCity() ?? 'Ukendt';
|
||||||
|
$recipient = [
|
||||||
|
'name' => $customer_name,
|
||||||
|
'address' => $customer_address,
|
||||||
|
'zip' => $customer_zip,
|
||||||
|
'city' => $customer_city,
|
||||||
|
'vatZone' => [
|
||||||
|
'vatZoneNumber' => (int)$customer->getVatZoneNumber(),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$customer_ean = $customer->getEan();
|
||||||
|
if ($customer_ean !== null) {
|
||||||
|
$recipient['ean'] = $customer_ean;
|
||||||
|
}
|
||||||
|
$public_entry_number = $customer->getPublicEntryNumber();
|
||||||
|
if ($public_entry_number !== null) {
|
||||||
|
$recipient['publicEntryNumber'] = $public_entry_number;
|
||||||
|
}
|
||||||
|
|
||||||
// Send the request
|
// Send the request
|
||||||
$response = $this->send_request(
|
$response = $this->send_request(
|
||||||
@@ -165,15 +182,7 @@ class economic_invoices_drafts_endpoint
|
|||||||
'currency' => $customer->getCurrency() ?? 'DKK',
|
'currency' => $customer->getCurrency() ?? 'DKK',
|
||||||
|
|
||||||
// Set the recipient details
|
// Set the recipient details
|
||||||
'recipient' => [
|
'recipient' => $recipient,
|
||||||
'name' => $customer_name,
|
|
||||||
'address' => $customer_address,
|
|
||||||
'zip' => $customer_zip,
|
|
||||||
'city' => $customer_city,
|
|
||||||
'vatZone' => [
|
|
||||||
'vatZoneNumber' => (int)$customer->getVatZoneNumber(),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
// Return the response as an object
|
// Return the response as an object
|
||||||
@@ -194,4 +203,4 @@ class economic_invoices_drafts_endpoint
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,6 +149,29 @@ class economic_customer
|
|||||||
return $this->customer_data_object->email;
|
return $this->customer_data_object->email;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getEan(): ?string
|
||||||
|
{
|
||||||
|
self::requireSelected();
|
||||||
|
return $this->nullableStringField('ean');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPublicEntryNumber(): ?string
|
||||||
|
{
|
||||||
|
self::requireSelected();
|
||||||
|
return $this->nullableStringField('publicEntryNumber');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function nullableStringField(string $field): ?string
|
||||||
|
{
|
||||||
|
$value = $this->customer_data_object->{$field} ?? null;
|
||||||
|
if ($value === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$normalized = trim((string)$value);
|
||||||
|
return $normalized !== '' ? $normalized : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the customer address
|
* Get the customer address
|
||||||
* @return string The customer address
|
* @return string The customer address
|
||||||
@@ -227,4 +250,4 @@ class economic_customer
|
|||||||
return $this->customer_data_object->vatZone->vatZoneNumber;
|
return $this->customer_data_object->vatZone->vatZoneNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2595,6 +2595,12 @@ paths:
|
|||||||
type: string
|
type: string
|
||||||
description: Contact person name
|
description: Contact person name
|
||||||
example: "Mikkel"
|
example: "Mikkel"
|
||||||
|
ean:
|
||||||
|
type: string
|
||||||
|
description: Optional EAN used for e-invoicing in e-conomic
|
||||||
|
maxLength: 13
|
||||||
|
pattern: '^[0-9]{1,13}$'
|
||||||
|
example: "5790001234567"
|
||||||
g_recaptcha_response:
|
g_recaptcha_response:
|
||||||
type: string
|
type: string
|
||||||
description: reCAPTCHA verification token
|
description: reCAPTCHA verification token
|
||||||
@@ -8653,6 +8659,12 @@ paths:
|
|||||||
email: {type: string}
|
email: {type: string}
|
||||||
phone: {type: integer}
|
phone: {type: integer}
|
||||||
name: {type: string}
|
name: {type: string}
|
||||||
|
ean:
|
||||||
|
type: string
|
||||||
|
description: Optional EAN used for e-invoicing in e-conomic
|
||||||
|
maxLength: 13
|
||||||
|
pattern: '^[0-9]{1,13}$'
|
||||||
|
example: "5790001234567"
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Success
|
description: Success
|
||||||
|
|||||||
@@ -427,6 +427,7 @@ class authRoute
|
|||||||
$contactEmail = self::getParameter('contactEmail');
|
$contactEmail = self::getParameter('contactEmail');
|
||||||
$contactPhone = (int)self::getParameter('contactPhone');
|
$contactPhone = (int)self::getParameter('contactPhone');
|
||||||
$contactName = self::getParameter('contactName');
|
$contactName = self::getParameter('contactName');
|
||||||
|
$ean = null;
|
||||||
/**
|
/**
|
||||||
* Validate
|
* Validate
|
||||||
*/
|
*/
|
||||||
@@ -454,6 +455,13 @@ class authRoute
|
|||||||
self::requireMinValue($contactPhone, 10000000);
|
self::requireMinValue($contactPhone, 10000000);
|
||||||
self::requireMaxValue($contactPhone, 9999999999);
|
self::requireMaxValue($contactPhone, 9999999999);
|
||||||
}
|
}
|
||||||
|
if (self::isParametersSet(['ean'])) {
|
||||||
|
try {
|
||||||
|
$ean = economic::normalizeCustomerEan(self::getParameter('ean'));
|
||||||
|
} catch (\InvalidArgumentException $exception) {
|
||||||
|
$response->error($exception->getMessage(), 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the contact phone is empty, default to company phone
|
* If the contact phone is empty, default to company phone
|
||||||
@@ -545,6 +553,7 @@ class authRoute
|
|||||||
(int)$companyPhone,
|
(int)$companyPhone,
|
||||||
(int)$contactPhone,
|
(int)$contactPhone,
|
||||||
$companyInformation,
|
$companyInformation,
|
||||||
|
$ean,
|
||||||
);
|
);
|
||||||
} catch (Exception $exception) {
|
} catch (Exception $exception) {
|
||||||
$recoveredCustomer = $this->recoverRegistrationAfterCreateFailure(
|
$recoveredCustomer = $this->recoverRegistrationAfterCreateFailure(
|
||||||
|
|||||||
@@ -60,6 +60,14 @@ class moduleEconomicCustomerRoute
|
|||||||
self::requireMaxLength('phone', 255);
|
self::requireMaxLength('phone', 255);
|
||||||
self::requireMinLength('name', 1);
|
self::requireMinLength('name', 1);
|
||||||
self::requireMaxLength('name', 255);
|
self::requireMaxLength('name', 255);
|
||||||
|
$ean = null;
|
||||||
|
if (self::isParametersSet(['ean'])) {
|
||||||
|
try {
|
||||||
|
$ean = economic::normalizeCustomerEan(self::getParameter('ean'));
|
||||||
|
} catch (\InvalidArgumentException $exception) {
|
||||||
|
$response->error($exception->getMessage(), 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
(new logs_o())->add('modules_economic', 'global', 1, 0, 'MODULES_ECONOMIC', 'User accessed the customer');
|
(new logs_o())->add('modules_economic', 'global', 1, 0, 'MODULES_ECONOMIC', 'User accessed the customer');
|
||||||
$result = (new economic())->createCustomer(
|
$result = (new economic())->createCustomer(
|
||||||
(int)self::getParameter('customer_number'),
|
(int)self::getParameter('customer_number'),
|
||||||
@@ -67,6 +75,9 @@ class moduleEconomicCustomerRoute
|
|||||||
(int)self::getParameter('cvr'),
|
(int)self::getParameter('cvr'),
|
||||||
(string)self::getParameter('email'),
|
(string)self::getParameter('email'),
|
||||||
(int)self::getParameter('phone'),
|
(int)self::getParameter('phone'),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
$ean,
|
||||||
);
|
);
|
||||||
$response->success((object)$result);
|
$response->success((object)$result);
|
||||||
} else {
|
} else {
|
||||||
@@ -76,4 +87,4 @@ class moduleEconomicCustomerRoute
|
|||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ it('returns the raw upstream create response and preserves the requested payload
|
|||||||
expect($probe->inner->lastPayload['phone'])->toBe(42331123);
|
expect($probe->inner->lastPayload['phone'])->toBe(42331123);
|
||||||
expect($probe->inner->lastPayload['telephoneAndFaxNumber'])->toBe('42331123');
|
expect($probe->inner->lastPayload['telephoneAndFaxNumber'])->toBe('42331123');
|
||||||
expect($probe->inner->lastPayload['mobilePhone'])->toBe('42331123');
|
expect($probe->inner->lastPayload['mobilePhone'])->toBe('42331123');
|
||||||
|
expect(array_key_exists('ean', $probe->inner->lastPayload))->toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds supported CVR company fields to the e-conomic customer payload', function (): void {
|
it('adds supported CVR company fields to the e-conomic customer payload', function (): void {
|
||||||
@@ -97,3 +98,46 @@ it('adds supported CVR company fields to the e-conomic customer payload', functi
|
|||||||
expect($probe->inner->lastPayload['mobilePhone'])->toBe('55667788');
|
expect($probe->inner->lastPayload['mobilePhone'])->toBe('55667788');
|
||||||
expect(array_key_exists('industrycode', $probe->inner->lastPayload))->toBeFalse();
|
expect(array_key_exists('industrycode', $probe->inner->lastPayload))->toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('adds a normalized EAN to the e-conomic customer payload when provided', function (): void {
|
||||||
|
$stubResponse = (object)[
|
||||||
|
'customerNumber' => 42331123,
|
||||||
|
'name' => 'Truckwash ApS',
|
||||||
|
];
|
||||||
|
|
||||||
|
$probe = new EconomicCreateCustomerProbe($stubResponse);
|
||||||
|
$probe->createCustomer(
|
||||||
|
42331123,
|
||||||
|
'Truckwash ApS',
|
||||||
|
37781258,
|
||||||
|
'invoice@truckwash.test',
|
||||||
|
42331123,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'57 90-001234567',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect($probe->inner->lastPayload['ean'])->toBe('5790001234567');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects EAN values longer than e-conomic accepts', function (): void {
|
||||||
|
$stubResponse = (object)[
|
||||||
|
'customerNumber' => 42331123,
|
||||||
|
'name' => 'Truckwash ApS',
|
||||||
|
];
|
||||||
|
|
||||||
|
$probe = new EconomicCreateCustomerProbe($stubResponse);
|
||||||
|
$call = static fn() => $probe->createCustomer(
|
||||||
|
42331123,
|
||||||
|
'Truckwash ApS',
|
||||||
|
37781258,
|
||||||
|
'invoice@truckwash.test',
|
||||||
|
42331123,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'57900012345678',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect($call)->toThrow(InvalidArgumentException::class, 'EAN must be at most 13 digits.');
|
||||||
|
expect($probe->inner->lastPayload)->toBe([]);
|
||||||
|
});
|
||||||
|
|||||||
@@ -176,6 +176,22 @@ it('creates a new e-conomic customer and returns a created result for new rows',
|
|||||||
expect($result['has_account'])->toBeFalse();
|
expect($result['has_account'])->toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('rejects EAN values longer than e-conomic accepts before creating customers', function (): void {
|
||||||
|
$service = new CustomerMassImportServiceProbe();
|
||||||
|
|
||||||
|
$call = static fn() => $service->import([
|
||||||
|
'cvr' => '29424764',
|
||||||
|
'name' => 'TGP TRANSPORT APS',
|
||||||
|
'email' => 'tgp@example.com',
|
||||||
|
'ean' => '57900012345678',
|
||||||
|
'phone' => '22725567',
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect($call)->toThrow(RuntimeException::class, 'EAN must be at most 13 digits.');
|
||||||
|
expect($service->createCalls)->toBe([]);
|
||||||
|
expect($service->bootstrapCalls)->toBe([]);
|
||||||
|
});
|
||||||
|
|
||||||
it('creates the economic record for an existing local account when no matching upstream customer exists', function (): void {
|
it('creates the economic record for an existing local account when no matching upstream customer exists', function (): void {
|
||||||
$service = new CustomerMassImportServiceProbe();
|
$service = new CustomerMassImportServiceProbe();
|
||||||
$service->localExists = true;
|
$service->localExists = true;
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
app_require('modules/economic/helpers/economic_customer.php');
|
||||||
|
|
||||||
|
use helpers\economic_customer;
|
||||||
|
|
||||||
|
function economic_customer_helper_from_payload(object $payload): economic_customer
|
||||||
|
{
|
||||||
|
$reflection = new ReflectionClass(economic_customer::class);
|
||||||
|
/** @var economic_customer $customer */
|
||||||
|
$customer = $reflection->newInstanceWithoutConstructor();
|
||||||
|
|
||||||
|
$property = $reflection->getProperty('customer_data_object');
|
||||||
|
$property->setAccessible(true);
|
||||||
|
$property->setValue($customer, $payload);
|
||||||
|
|
||||||
|
return $customer;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('exposes optional EAN and public entry number from fetched e-conomic customer data', function (): void {
|
||||||
|
$customer = economic_customer_helper_from_payload((object)[
|
||||||
|
'customerNumber' => 42331123,
|
||||||
|
'ean' => ' 5790001234567 ',
|
||||||
|
'publicEntryNumber' => ' DK123456789 ',
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect($customer->getEan())->toBe('5790001234567');
|
||||||
|
expect($customer->getPublicEntryNumber())->toBe('DK123456789');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns null for blank optional e-conomic customer recipient identifiers', function (): void {
|
||||||
|
$customer = economic_customer_helper_from_payload((object)[
|
||||||
|
'customerNumber' => 42331123,
|
||||||
|
'ean' => ' ',
|
||||||
|
'publicEntryNumber' => '',
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect($customer->getEan())->toBeNull();
|
||||||
|
expect($customer->getPublicEntryNumber())->toBeNull();
|
||||||
|
});
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
function economic_ean_openapi_content_or_skip(): string
|
||||||
|
{
|
||||||
|
$candidates = [];
|
||||||
|
for ($depth = 1; $depth <= 8; $depth++) {
|
||||||
|
$candidates[] = dirname(__DIR__, $depth) . DIRECTORY_SEPARATOR . 'openapi.yaml';
|
||||||
|
}
|
||||||
|
|
||||||
|
$cwd = getcwd();
|
||||||
|
if (is_string($cwd) && $cwd !== '') {
|
||||||
|
$candidates[] = $cwd . DIRECTORY_SEPARATOR . 'openapi.yaml';
|
||||||
|
$candidates[] = dirname($cwd) . DIRECTORY_SEPARATOR . 'openapi.yaml';
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (array_values(array_unique($candidates)) as $candidate) {
|
||||||
|
if (is_file($candidate)) {
|
||||||
|
$content = file_get_contents($candidate);
|
||||||
|
if ($content !== false) {
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test()->markTestSkipped('openapi.yaml is not available in this runtime environment.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function economic_ean_openapi_block(string $content, string $start, string $end): string
|
||||||
|
{
|
||||||
|
$start_pos = strpos($content, $start);
|
||||||
|
$end_pos = strpos($content, $end);
|
||||||
|
expect($start_pos)->not->toBeFalse();
|
||||||
|
expect($end_pos)->not->toBeFalse();
|
||||||
|
expect($end_pos)->toBeGreaterThan($start_pos);
|
||||||
|
|
||||||
|
return substr($content, (int)$start_pos, (int)$end_pos - (int)$start_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
it('documents optional EAN on customer creation endpoints', function (): void {
|
||||||
|
$content = economic_ean_openapi_content_or_skip();
|
||||||
|
|
||||||
|
$register_block = economic_ean_openapi_block($content, '/auth/register/cvr:', '/auth/password-reset/request:');
|
||||||
|
$economic_customer_block = economic_ean_openapi_block($content, '/modules/economic/customer:', '/economic/layouts:');
|
||||||
|
|
||||||
|
foreach ([$register_block, $economic_customer_block] as $block) {
|
||||||
|
expect($block)->toContain('ean:');
|
||||||
|
expect($block)->toContain('maxLength: 13');
|
||||||
|
expect($block)->toContain("pattern: '^[0-9]{1,13}$'");
|
||||||
|
}
|
||||||
|
});
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
it('wires EAN and public entry number into e-conomic invoice draft recipients', function (): void {
|
||||||
|
$content = file_get_contents(app_path('modules/economic/endpoints/invoices/economic_invoices_drafts_endpoint.php'));
|
||||||
|
|
||||||
|
expect($content)->not->toBeFalse();
|
||||||
|
expect($content)->toContain('$customer->getEan()');
|
||||||
|
expect($content)->toContain("\$recipient['ean']");
|
||||||
|
expect($content)->toContain('$customer->getPublicEntryNumber()');
|
||||||
|
expect($content)->toContain("\$recipient['publicEntryNumber']");
|
||||||
|
expect($content)->toContain("'recipient' => \$recipient");
|
||||||
|
});
|
||||||
@@ -18,6 +18,8 @@ it('parses cached economic customer payloads that use snake_case customer_number
|
|||||||
'customer_number' => '42331123',
|
'customer_number' => '42331123',
|
||||||
'name' => 'Truckwash ApS',
|
'name' => 'Truckwash ApS',
|
||||||
'email' => 'jb@truckwash.dk',
|
'email' => 'jb@truckwash.dk',
|
||||||
|
'ean' => '5790001234567',
|
||||||
|
'public_entry_number' => 'DK123456789',
|
||||||
'currency' => 'DKK',
|
'currency' => 'DKK',
|
||||||
'country' => 'DK',
|
'country' => 'DK',
|
||||||
'barred' => true,
|
'barred' => true,
|
||||||
@@ -31,6 +33,8 @@ it('parses cached economic customer payloads that use snake_case customer_number
|
|||||||
'zip' => null,
|
'zip' => null,
|
||||||
'corporateIdentificationNumber' => null,
|
'corporateIdentificationNumber' => null,
|
||||||
'email' => 'jb@truckwash.dk',
|
'email' => 'jb@truckwash.dk',
|
||||||
|
'ean' => '5790001234567',
|
||||||
|
'publicEntryNumber' => 'DK123456789',
|
||||||
'mobilePhone' => null,
|
'mobilePhone' => null,
|
||||||
'currency' => 'DKK',
|
'currency' => 'DKK',
|
||||||
'country' => 'DK',
|
'country' => 'DK',
|
||||||
|
|||||||
@@ -105,7 +105,30 @@ namespace classes {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createCustomer($number, $name, $cvr, $email, $phone, $mobilePhone = null, $companyInformation = null): object
|
public static function normalizeCustomerEan(mixed $value): ?string
|
||||||
|
{
|
||||||
|
if ($value === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$digits = preg_replace('/\D+/', '', (string)$value);
|
||||||
|
if (!is_string($digits)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$digits = trim($digits);
|
||||||
|
if ($digits === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($digits) > 13) {
|
||||||
|
throw new \InvalidArgumentException('EAN must be at most 13 digits.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $digits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createCustomer($number, $name, $cvr, $email, $phone, $mobilePhone = null, $companyInformation = null, $ean = null): object
|
||||||
{
|
{
|
||||||
self::$create_calls[] = [
|
self::$create_calls[] = [
|
||||||
'number' => (int)$number,
|
'number' => (int)$number,
|
||||||
@@ -115,6 +138,7 @@ namespace classes {
|
|||||||
'phone' => (int)$phone,
|
'phone' => (int)$phone,
|
||||||
'mobile_phone' => $mobilePhone === null ? null : (int)$mobilePhone,
|
'mobile_phone' => $mobilePhone === null ? null : (int)$mobilePhone,
|
||||||
'company_information' => $companyInformation,
|
'company_information' => $companyInformation,
|
||||||
|
'ean' => $ean === null ? null : (string)$ean,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (self::$mock_create_exception !== null) {
|
if (self::$mock_create_exception !== null) {
|
||||||
@@ -424,6 +448,16 @@ namespace {
|
|||||||
'expected_error' => 'Parameter cvr must be at least 8 characters long',
|
'expected_error' => 'Parameter cvr must be at least 8 characters long',
|
||||||
'expected_status' => 400,
|
'expected_status' => 400,
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'name' => 'Invalid EAN length (too long)',
|
||||||
|
'params' => array_merge($baseParams, ['ean' => '57900012345678']),
|
||||||
|
'expected_error' => 'EAN must be at most 13 digits.',
|
||||||
|
'expected_status' => 400,
|
||||||
|
'assert' => static function (): void {
|
||||||
|
assert_true(count(\classes\economic::$create_calls) === 0, 'Invalid EAN must not create e-conomic customers.');
|
||||||
|
assert_true(count(\classes\email::$sent) === 0, 'Invalid EAN must not send welcome emails.');
|
||||||
|
},
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'name' => 'CVR lookup failure returns validation error without creating customer',
|
'name' => 'CVR lookup failure returns validation error without creating customer',
|
||||||
'params' => array_merge($baseParams, ['cvr' => '11111112']),
|
'params' => array_merge($baseParams, ['cvr' => '11111112']),
|
||||||
@@ -563,7 +597,7 @@ namespace {
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'Successful registration bootstraps local user before welcome emails',
|
'name' => 'Successful registration bootstraps local user before welcome emails',
|
||||||
'params' => array_merge($baseParams, ['contactPhone' => 87654320]),
|
'params' => array_merge($baseParams, ['contactPhone' => 87654320, 'ean' => '57 90-001234567']),
|
||||||
'setup' => static function (): void {
|
'setup' => static function (): void {
|
||||||
\classes\economic::$mock_create_response = (object)[
|
\classes\economic::$mock_create_response = (object)[
|
||||||
'customerNumber' => 12345678,
|
'customerNumber' => 12345678,
|
||||||
@@ -579,6 +613,7 @@ namespace {
|
|||||||
assert_true(count(\classes\economic::$create_calls) === 1, 'Fresh registration must call create exactly once.');
|
assert_true(count(\classes\economic::$create_calls) === 1, 'Fresh registration must call create exactly once.');
|
||||||
assert_true(\classes\economic::$create_calls[0]['phone'] === 12345678, 'Fresh registration must use the company phone as the e-conomic customer phone.');
|
assert_true(\classes\economic::$create_calls[0]['phone'] === 12345678, 'Fresh registration must use the company phone as the e-conomic customer phone.');
|
||||||
assert_true(\classes\economic::$create_calls[0]['mobile_phone'] === 87654320, 'Fresh registration must pass the contact phone as the e-conomic mobile phone.');
|
assert_true(\classes\economic::$create_calls[0]['mobile_phone'] === 87654320, 'Fresh registration must pass the contact phone as the e-conomic mobile phone.');
|
||||||
|
assert_true(\classes\economic::$create_calls[0]['ean'] === '5790001234567', 'Fresh registration must pass normalized EAN to e-conomic.');
|
||||||
$companyInformation = \classes\economic::$create_calls[0]['company_information'];
|
$companyInformation = \classes\economic::$create_calls[0]['company_information'];
|
||||||
assert_true(is_object($companyInformation), 'Fresh registration must pass CVR company information to e-conomic.');
|
assert_true(is_object($companyInformation), 'Fresh registration must pass CVR company information to e-conomic.');
|
||||||
assert_true($companyInformation->address === 'Demo Street 1', 'Fresh registration must pass the CVR address to e-conomic.');
|
assert_true($companyInformation->address === 'Demo Street 1', 'Fresh registration must pass the CVR address to e-conomic.');
|
||||||
|
|||||||
Reference in New Issue
Block a user