diff --git a/Api/Config/RepositoryInterface.php b/Api/Config/RepositoryInterface.php
index 24bb771d..f42eb496 100755
--- a/Api/Config/RepositoryInterface.php
+++ b/Api/Config/RepositoryInterface.php
@@ -30,6 +30,7 @@ interface RepositoryInterface
public const XML_PATH_ENABLE_ADDRESS_SEARCH = 'payment/two_payment/enable_address_search';
public const XML_PATH_ENABLE_TAX_SUBTOTALS = 'payment/two_payment/enable_tax_subtotals';
public const XML_PATH_ENABLE_ORDER_INTENT = 'payment/two_payment/enable_order_intent';
+ public const XML_PATH_ENABLE_MULTIPLE_INVOICE_EMAILS = 'payment/two_payment/enable_multiple_invoice_emails';
public const XML_PATH_ENABLE_DEPARTMENT_NAME = 'payment/two_payment/enable_department';
public const XML_PATH_ENABLE_PROJECT_NAME = 'payment/two_payment/enable_project';
public const XML_PATH_ENABLE_ORDER_NOTE = 'payment/two_payment/enable_order_note';
@@ -122,6 +123,15 @@ public function isCompanySearchEnabled(?int $storeId = null): bool;
*/
public function isOrderIntentEnabled(?int $storeId = null): bool;
+ /**
+ * Check if order intent is enabled
+ *
+ * @param int|null $storeId
+ *
+ * @return bool
+ */
+ public function isMultipleInvoiceEmailsEnabled(?int $storeId = null): bool;
+
/**
* Check if tax subtotals is enabled
*
diff --git a/Model/Config/Repository.php b/Model/Config/Repository.php
index e195bb94..f6859dd5 100755
--- a/Model/Config/Repository.php
+++ b/Model/Config/Repository.php
@@ -156,6 +156,14 @@ public function isOrderIntentEnabled(?int $storeId = null): bool
return $this->isSetFlag(self::XML_PATH_ENABLE_ORDER_INTENT, $storeId);
}
+ /**
+ * @inheritDoc
+ */
+ public function isMultipleInvoiceEmailsEnabled(?int $storeId = null): bool
+ {
+ return $this->isSetFlag(self::XML_PATH_ENABLE_MULTIPLE_INVOICE_EMAILS, $storeId);
+ }
+
/**
* @inheritDoc
*/
diff --git a/Model/Two.php b/Model/Two.php
index 2ee04e5c..b89e1840 100755
--- a/Model/Two.php
+++ b/Model/Two.php
@@ -204,12 +204,19 @@ public function authorize(InfoInterface $payment, $amount)
$order = $payment->getOrder();
$this->urlCookie->delete();
$orderReference = (string)rand();
+
+ $additionalInformation = $payment->getAdditionalInformation();
+
+ $multipleInvoiceEmails = $additionalInformation['multipleInvoiceEmails'] ?? null;
+
$payload = $this->compositeOrder->execute(
$order,
$orderReference,
- $payment->getAdditionalInformation()
+ $additionalInformation
);
+
+
// Create order
$response = $this->apiAdapter->execute('/v1/order', $payload);
$error = $this->getErrorFromResponse($response);
diff --git a/Model/Ui/ConfigProvider.php b/Model/Ui/ConfigProvider.php
index 057c6c06..30d2c996 100755
--- a/Model/Ui/ConfigProvider.php
+++ b/Model/Ui/ConfigProvider.php
@@ -91,6 +91,7 @@ public function getConfig(): array
'checkoutPageUrl' => $this->configRepository->getCheckoutPageUrl(),
'redirectUrlCookieCode' => UrlCookie::COOKIE_NAME,
'isOrderIntentEnabled' => $this->configRepository->isOrderIntentEnabled(),
+ 'isMultipleInvoiceEmailsEnabled' => $this->configRepository->isMultipleInvoiceEmailsEnabled(),
'orderIntentConfig' => $orderIntentConfig,
'isCompanySearchEnabled' => $this->configRepository->isCompanySearchEnabled(),
'isAddressSearchEnabled' => $this->configRepository->isAddressSearchEnabled(),
@@ -115,6 +116,7 @@ public function getConfig(): array
$provider,
$tryAgainLater
),
+ 'invalidEmailListMessage' => __('Please ensure your forward to email list only contains valid emails seperated by commas.'),
'paymentTermsMessage' => __(
'By checking this box, I confirm that I have read and agree to %1.',
sprintf('%s', $paymentTermsLink, $paymentTerms)
diff --git a/Observer/DataAssignObserver.php b/Observer/DataAssignObserver.php
index e91d0ed5..7fc7abdd 100755
--- a/Observer/DataAssignObserver.php
+++ b/Observer/DataAssignObserver.php
@@ -24,6 +24,7 @@ class DataAssignObserver extends AbstractDataAssignObserver
'department',
'orderNote',
'poNumber',
+ 'multipleInvoiceEmails'
];
/**
diff --git a/Service/Order/ComposeOrder.php b/Service/Order/ComposeOrder.php
index 00aad44a..8dd8513a 100755
--- a/Service/Order/ComposeOrder.php
+++ b/Service/Order/ComposeOrder.php
@@ -27,41 +27,61 @@ class ComposeOrder extends OrderService
* @throws LocalizedException
*/
public function execute(Order $order, string $orderReference, array $additionalData): array
- {
- $lineItems = $this->getLineItemsOrder($order);
+{
+ // Fetch line items from the order
+ $lineItems = $this->getLineItemsOrder($order);
+
+
+ // Initialize invoice_details only if multipleInvoiceEmails is present
+ $invoiceDetails = [];
+ if (!empty($additionalData['multipleInvoiceEmails'])) {
+ $invoiceDetails['invoice_emails'] = explode(',', $additionalData['multipleInvoiceEmails']);
+ // Required placeholders to pass create order API Schema requirements
+ $invoiceDetails['payment_reference_message'] = "";
+ $invoiceDetails['payment_reference_ocr'] = "";
+ }
+
+ // Compose the final payload for the API call
+ $payload = [
+ 'billing_address' => $this->getAddress($order, $additionalData, 'billing'),
+ 'shipping_address' => $this->getAddress($order, $additionalData, 'shipping'),
+ 'buyer' => $this->getBuyer($order, $additionalData),
+ 'buyer_department' => $additionalData['department'] ?? '',
+ 'buyer_project' => $additionalData['project'] ?? '',
+ 'buyer_purchase_order_number' => $additionalData['poNumber'] ?? '',
+ 'currency' => $order->getOrderCurrencyCode(),
+ 'discount_amount' => $this->roundAmt($this->getDiscountAmountItem($order)),
+ 'gross_amount' => $this->roundAmt($order->getGrandTotal()),
+ 'net_amount' => $this->roundAmt($order->getGrandTotal() - $order->getTaxAmount()),
+ 'tax_amount' => $this->roundAmt($order->getTaxAmount()),
+ 'tax_subtotals' => $this->getTaxSubtotals($lineItems),
+ 'invoice_type' => 'FUNDED_INVOICE',
+ 'line_items' => $lineItems,
+ 'merchant_order_id' => (string)($order->getIncrementId()),
+ 'merchant_urls' => [
+ 'merchant_confirmation_url' => $this->url->getUrl(
+ 'two/payment/confirm',
+ ['_two_order_reference' => base64_encode($orderReference)]
+ ),
+ 'merchant_cancel_order_url' => $this->url->getUrl(
+ 'two/payment/cancel',
+ ['_two_order_reference' => base64_encode($orderReference)]
+ ),
+ 'merchant_edit_order_url' => '',
+ 'merchant_order_verification_failed_url' => $this->url->getUrl(
+ 'two/payment/verificationfailed',
+ ['_two_order_reference' => base64_encode($orderReference)]
+ ),
+ ],
+ 'order_note' => $additionalData['orderNote'] ?? ''
+ ];
+
+ // Add invoice_details only if invoiceEmails are present
+ if (!empty($invoiceDetails)) {
+ $payload['invoice_details'] = $invoiceDetails;
+ }
+
- return [
- 'billing_address' => $this->getAddress($order, $additionalData, 'billing'),
- 'shipping_address' => $this->getAddress($order, $additionalData, 'shipping'),
- 'buyer' => $this->getBuyer($order, $additionalData),
- 'buyer_department' => $additionalData['department'] ?? '',
- 'buyer_project' => $additionalData['project'] ?? '',
- 'buyer_purchase_order_number' => $additionalData['poNumber'] ?? '',
- 'currency' => $order->getOrderCurrencyCode(),
- 'discount_amount' => $this->roundAmt($this->getDiscountAmountItem($order)),
- 'gross_amount' => $this->roundAmt($order->getGrandTotal()),
- 'net_amount' => $this->roundAmt($order->getGrandTotal() - $order->getTaxAmount()),
- 'tax_amount' => $this->roundAmt($order->getTaxAmount()),
- 'tax_subtotals' => $this->getTaxSubtotals($lineItems),
- 'invoice_type' => 'FUNDED_INVOICE',
- 'line_items' => $lineItems,
- 'merchant_order_id' => (string)($order->getIncrementId()),
- 'merchant_urls' => [
- 'merchant_confirmation_url' => $this->url->getUrl(
- 'two/payment/confirm',
- ['_two_order_reference' => base64_encode($orderReference)]
- ),
- 'merchant_cancel_order_url' => $this->url->getUrl(
- 'two/payment/cancel',
- ['_two_order_reference' => base64_encode($orderReference)]
- ),
- 'merchant_edit_order_url' => '',
- 'merchant_order_verification_failed_url' => $this->url->getUrl(
- 'two/payment/verificationfailed',
- ['_two_order_reference' => base64_encode($orderReference)]
- ),
- ],
- 'order_note' => $additionalData['orderNote'] ?? ''
- ];
+ return $payload;
}
}
diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml
index 4dc7df6d..18e9a983 100755
--- a/etc/adminhtml/system.xml
+++ b/etc/adminhtml/system.xml
@@ -158,6 +158,16 @@
payment/two_payment/enable_order_intent
+
+
+ Let your buyer input additional emails to forward the invoice to on order fulfilment.
+ Magento\Config\Model\Config\Source\Yesno
+
+ 1
+
+ payment/two_payment/enable_multiple_invoice_emails
+
diff --git a/etc/config.xml b/etc/config.xml
index dc7d6256..ee63cede 100755
--- a/etc/config.xml
+++ b/etc/config.xml
@@ -18,6 +18,7 @@
14
1
1
+ 0
1
shipment
complete
@@ -35,4 +36,3 @@
-
diff --git a/i18n/en_US.csv b/i18n/en_US.csv
index bac1c0a0..14086f87 100644
--- a/i18n/en_US.csv
+++ b/i18n/en_US.csv
@@ -113,3 +113,5 @@ Version,Version
"Your request to %1 failed. Reason: %2","Your request to %1 failed. Reason: %2"
"Your sole trader account could not be verified.","Your sole trader account could not be verified."
"Zip/Postal Code is not valid.","Zip/Postal Code is not valid."
+"Forward invoice to email list (optional)","Forward invoice to email list (optional)"
+"Please ensure your forward to email list only contains valid emails seperated by commas.", "Please ensure your forward to email list only contains valid emails seperated by commas."
diff --git a/i18n/nb_NO.csv b/i18n/nb_NO.csv
index aca71026..feac3df4 100644
--- a/i18n/nb_NO.csv
+++ b/i18n/nb_NO.csv
@@ -113,3 +113,5 @@ Version,Versjon
"Your request to %1 failed. Reason: %2","Din forespørsel til %1 mislyktes. Årsak: %2"
"Your sole trader account could not be verified.","Din eneforhandlerkonto kunne ikke bekreftes."
"Zip/Postal Code is not valid.","Postnummer er ikke gyldig."
+"Forward invoice to email list (optional)","Videresend faktura til e-postliste (valgfritt)"
+"Please ensure your forward to email list only contains valid emails seperated by commas.","Sørg for at listen over videresending til e-post kun inneholder gyldige e-poster atskilt med komma."
diff --git a/i18n/nl_NL.csv b/i18n/nl_NL.csv
index cf7bec17..fb4400c1 100644
--- a/i18n/nl_NL.csv
+++ b/i18n/nl_NL.csv
@@ -113,3 +113,5 @@ Version,Versie
"Your request to %1 failed. Reason: %2","Uw verzoek aan %1 is mislukt. Reden: %2"
"Your sole trader account could not be verified.","Uw eenmanszaakaccount kon niet worden geverifieerd."
"Zip/Postal Code is not valid.","Postcode is niet geldig."
+"Forward invoice to email list (optional)","Factuur doorsturen naar e-maillijst (optioneel)"
+"Please ensure your forward to email list only contains valid emails seperated by commas.","Zorg ervoor dat uw doorstuur-naar-e-maillijst alleen geldige e-mails bevat, gescheiden door komma's."
diff --git a/i18n/sv_SE.csv b/i18n/sv_SE.csv
index bb6247a8..f8bf2de6 100644
--- a/i18n/sv_SE.csv
+++ b/i18n/sv_SE.csv
@@ -113,3 +113,5 @@ Version,Version
"Your request to %1 failed. Reason: %2","Din begäran till %1 misslyckades. Orsak: %2"
"Your sole trader account could not be verified.","Ditt enskild näringsidkarekonto kunde inte verifieras."
"Zip/Postal Code is not valid.","Postnummer är inte giltigt."
+"Forward invoice to email list (optional)","Vidarebefordra faktura till e-postlista (valfritt)"
+"Please ensure your forward to email list only contains valid emails seperated by commas.","Se till att din vidarebefordran till e-postlista endast innehåller giltiga e-postmeddelanden separerade med kommatecken."
diff --git a/view/frontend/web/js/view/payment/method-renderer/two_payment.js b/view/frontend/web/js/view/payment/method-renderer/two_payment.js
index e2f09413..9e59ab68 100755
--- a/view/frontend/web/js/view/payment/method-renderer/two_payment.js
+++ b/view/frontend/web/js/view/payment/method-renderer/two_payment.js
@@ -49,8 +49,10 @@ define([
orderIntentApprovedMessage: config.orderIntentApprovedMessage,
orderIntentDeclinedMessage: config.orderIntentDeclinedMessage,
generalErrorMessage: config.generalErrorMessage,
+ invalidEmailListMessage: config.invalidEmailListMessage,
soleTraderErrorMessage: config.soleTraderErrorMessage,
isOrderIntentEnabled: config.isOrderIntentEnabled,
+ isMultipleInvoiceEmailsEnabled: config.isMultipleInvoiceEmailsEnabled,
isDepartmentFieldEnabled: config.isDepartmentFieldEnabled,
isProjectFieldEnabled: config.isProjectFieldEnabled,
isOrderNoteFieldEnabled: config.isOrderNoteFieldEnabled,
@@ -67,6 +69,7 @@ define([
autofillToken: '',
companyName: ko.observable(''),
companyId: ko.observable(''),
+ multipleInvoiceEmails: ko.observable(''),
project: ko.observable(''),
department: ko.observable(''),
orderNote: ko.observable(''),
@@ -84,6 +87,28 @@ define([
this.configureFormValidation();
this.popupMessageListener();
},
+ showErrorMessage: function (message, duration) {
+ messageList.addErrorMessage({ message: message });
+
+ if (duration) {
+ setTimeout(function () {
+ messageList.messages.remove(function (item) {
+ return item.message === message;
+ });
+ }, duration);
+ }
+ },
+ validateMultipleEmails: function () {
+ const emails = this.multipleInvoiceEmails();
+ let emailArray = emails.split(',').map((email) => email.trim());
+
+ const isValid = emailArray.every((email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email));
+ if (!isValid && emails) {
+ this.showErrorMessage(this.invalidEmailListMessage, 3);
+ return false;
+ }
+ return true;
+ },
logIsPaymentsAccepted: function (data, event) {
console.debug({
logger: 'logIsPaymentsAccepted',
@@ -217,6 +242,13 @@ define([
this.processTermsNotAcceptedErrorResponse();
return;
}
+
+ // Validate emails on the forward list
+ if (this.isMultipleInvoiceEmailsEnabled && !this.validateMultipleEmails()) {
+ this.showErrorMessage(this.invalidEmailListMessage);
+ return;
+ }
+
if (
this.validate() &&
additionalValidators.validate() &&
@@ -392,7 +424,8 @@ define([
project: this.project(),
department: this.department(),
orderNote: this.orderNote(),
- poNumber: this.poNumber()
+ poNumber: this.poNumber(),
+ multipleInvoiceEmails: this.multipleInvoiceEmails()
}
};
},
diff --git a/view/frontend/web/template/payment/two_payment.html b/view/frontend/web/template/payment/two_payment.html
index 31f1ea05..4783bedc 100755
--- a/view/frontend/web/template/payment/two_payment.html
+++ b/view/frontend/web/template/payment/two_payment.html
@@ -172,6 +172,19 @@
/>
+