diff --git a/website_sale_signifyd/data/signifyd_coverage.xml b/website_sale_signifyd/data/signifyd_coverage.xml
index eaedcccb..652ddf37 100644
--- a/website_sale_signifyd/data/signifyd_coverage.xml
+++ b/website_sale_signifyd/data/signifyd_coverage.xml
@@ -18,10 +18,12 @@
All
Use when you need a financial guarantee on all chargebacks.
ALL
+ True
None
Use when you do not need a financial guarantee.
NONE
+ True
\ No newline at end of file
diff --git a/website_sale_signifyd/models/__init__.py b/website_sale_signifyd/models/__init__.py
index 96cb7f80..d59fa371 100644
--- a/website_sale_signifyd/models/__init__.py
+++ b/website_sale_signifyd/models/__init__.py
@@ -1,12 +1,12 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import delivery_carrier
-from . import partner
-from . import payment
+from . import res_partner
+from . import payment_acquirer
from . import product_template
from . import sale_order
from . import signifyd_case
from . import signifyd_coverage
from . import signifyd_connector
-from . import stock
+from . import stock_picking
from . import website
diff --git a/website_sale_signifyd/models/payment.py b/website_sale_signifyd/models/payment.py
deleted file mode 100644
index e9f875bd..00000000
--- a/website_sale_signifyd/models/payment.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
-
-from odoo import api, fields, models
-
-
-class PaymentAcquirer(models.Model):
- _inherit = 'payment.acquirer'
-
- # TODO: remove options no longer available in api v3
- signifyd_case_type = fields.Selection([
- ('', 'No Case'),
- ('SCORE', 'Score'),
- ('DECISION', 'Decision'),
- ('GUARANTEE', 'Guarantee'),
- ], string='Signifyd Case Creation', default='')
-
- signify_coverage_types = fields.Many2many('signifyd.coverage', string='Signifyd Coverage Types')
diff --git a/website_sale_signifyd/models/payment_acquirer.py b/website_sale_signifyd/models/payment_acquirer.py
new file mode 100644
index 00000000..3888aee7
--- /dev/null
+++ b/website_sale_signifyd/models/payment_acquirer.py
@@ -0,0 +1,27 @@
+# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+
+class PaymentAcquirer(models.Model):
+ _inherit = 'payment.acquirer'
+
+ # TODO: remove options no longer available in api v3
+ signifyd_case_type = fields.Selection([
+ ('', 'No Case'),
+ ('SCORE', 'Score'),
+ ('DECISION', 'Decision'),
+ ('GUARANTEE', 'Guarantee'),
+ ], string='Signifyd Case Creation', default='')
+ signifyd_case_required = fields.Boolean(string='Create Signifyd Case', default=True)
+ signifyd_coverage_ids = fields.Many2many('signifyd.coverage', string='Available Coverage Types',
+ help='Note that exclusive coverage types will only allow one to be selected.')
+
+ @api.onchange('signifyd_coverage_ids')
+ def _onchange_signifyd_coverage_ids(self):
+ self.signifyd_coverage_ids = self.signifyd_coverage_ids._apply_exclusivity()
+
+ @api.onchange('signifyd_case_required')
+ def _onchange_signifyd_case_required(self):
+ if not self.signifyd_case_required:
+ self.signifyd_coverage_ids = False
\ No newline at end of file
diff --git a/website_sale_signifyd/models/partner.py b/website_sale_signifyd/models/res_partner.py
similarity index 100%
rename from website_sale_signifyd/models/partner.py
rename to website_sale_signifyd/models/res_partner.py
diff --git a/website_sale_signifyd/models/sale_order.py b/website_sale_signifyd/models/sale_order.py
index bb76a8ac..ce9da46a 100644
--- a/website_sale_signifyd/models/sale_order.py
+++ b/website_sale_signifyd/models/sale_order.py
@@ -37,19 +37,10 @@ class SaleOrder(models.Model):
def _should_post_signifyd(self):
# If we have no transaction/acquirer we will still send!
# this case is useful for admin or free orders but could be customized here.
- # case_required = bool(self.website_id.signifyd_connector_id.signifyd_case_type)
- # a_case_types = self.transaction_ids.mapped('acquirer_id.signifyd_case_type')
- # if a_case_types:
- # case_required = any(a_case_types)
- # return self.state in ('sale', 'done') and not self.signifyd_case_id and case_required
-
- case_required = bool(self.website_id.signifyd_connector_id.signifyd_case_type)
- case_required = self.website_id.signifyd_connector_id.signifyd_case_type not in [
- self.env.ref('website_sale_signifyd.signifyd_coverage_none').id,
- False
- ]
- coverage_types = self.transaction_ids.signifyd_coverage_ids
-
+ acquirers = self.transaction_ids.acquirer_id
+ if acquirers and not any(acquirers.mapped('signifyd_case_required')):
+ return False
+ return True
def post_signifyd_case(self):
if not self.website_id.signifyd_connector_id:
@@ -78,8 +69,26 @@ class SaleOrder(models.Model):
# TODO do we need to raise an exception?
return None
+ def _get_coverage_types(self):
+ coverage_none = self.env.ref('website_sale_signifyd.signifyd_coverage_none')
+ coverage_all = self.env.ref('website_sale_signifyd.signifyd_coverage_all')
+ acquirer_coverage_types = self.transaction_ids.acquirer_id.signifyd_coverage_ids
+ # 'ALL' if specified by any acquirer
+ if coverage_all in acquirer_coverage_types:
+ return coverage_all
+ # 'NONE' if specified by all acquirers
+ if all(self.transaction_ids.acquirer_id.mapped(lambda a: a.signifyd_coverage_ids) == coverage_none):
+ return coverage_none
+ # Specific acquirer-level coverage types
+ if acquirer_coverage_types - coverage_none:
+ return acquirer_coverage_types - coverage_none
+ # Default: connector-level
+ return self.website_id.signifyd_connector_id.signifyd_coverage_ids or coverage_none
+
@api.model
def _prepare_signifyd_case_values(self, order_session_id, checkout_token, browser_ip_address):
+ coverage_codes = self._get_coverage_types().mapped('code')
+
decision_request = self.website_id.signifyd_connector_id.signifyd_case_type or 'DECISION'
# find the highest 'acquirer override'
@@ -102,6 +111,7 @@ class SaleOrder(models.Model):
}
recipients = self.partner_invoice_id + self.partner_shipping_id
+ # API v3 WIP
new_case_vals = {
# FIXME: UUID?
'orderId': self.id,
@@ -132,7 +142,7 @@ class SaleOrder(models.Model):
'fulfillmentMethod': carrier.signifyd_fulfillment_method,
} for carrier in self.carrier_id
],
- 'coverageRequests'
+ 'coverageRequests': coverage_codes,
}
@@ -142,82 +152,83 @@ class SaleOrder(models.Model):
if not line[key]:
line.pop(key)
- new_case_vals = {
- 'decisionRequest': {
- 'paymentFraud': decision_request,
- },
- 'purchase': {
- "orderSessionId": order_session_id,
- "orderId": self.id,
- "checkoutToken": checkout_token,
- "browserIpAddress": browser_ip_address,
- "currency": self.partner_id.currency_id.name,
- "orderChannel": "WEB",
- "totalPrice": self.amount_total,
- 'products': [
- {
- "itemId": line.product_id.id,
- "itemName": line.product_id.name,
- "itemIsDigital": False,
- "itemCategory": line.product_id.categ_id.name,
- "itemUrl": line.product_id.website_url or '',
- "itemQuantity": line.product_uom_qty,
- "itemPrice": line.price_unit,
- "itemWeight": line.product_id.weight or 0.1,
- }
- for line in self.order_line if line.product_id
- ],
- 'shipments': [{
- "shipper": carrier.name,
- "shippingMethod": "ground",
- "shippingPrice": self.amount_delivery,
- }
- for carrier in self.carrier_id
- ],
- },
- 'recipients': [
- {
- "fullName": partner.name,
- "confirmationEmail": partner.email,
- "confirmationPhone": partner.phone,
- "organization": partner.company_id.name,
- "deliveryAddress": {
- "streetAddress": partner.street,
- "unit": partner.street2,
- "city": partner.city,
- "provinceCode": partner.state_id.code,
- "postalCode": partner.zip,
- "countryCode": partner.country_id.code,
- }
- }
- for partner in recipients
- ],
- 'transactions': [
- {
- "parentTransactionId": None,
- "transactionId": tx.id,
- "gateway": tx.acquirer_id.name,
- "paymentMethod": "CREDIT_CARD",
- "gatewayStatusCode": tx_status_type.get(tx.state, 'PENDING'),
- "type": "AUTHORIZATION",
- "currency": self.partner_id.currency_id.name,
- "amount": tx.amount,
- # "avsResponseCode": "Y",
- # "cvvResponseCode": "N",
- "checkoutPaymentDetails": {
- "holderName": tx.partner_id.name,
- "billingAddress": {
- "streetAddress": tx.partner_id.street,
- "unit": tx.partner_id.street2,
- "city": tx.partner_id.city,
- "provinceCode": tx.partner_id.state_id.code,
- "postalCode": tx.partner_id.zip,
- "countryCode": tx.partner_id.country_id.code,
- }
- }
- }
- for tx in self.transaction_ids
- ],
- }
+ # API v2
+ # new_case_vals = {
+ # 'decisionRequest': {
+ # 'paymentFraud': decision_request,
+ # },
+ # 'purchase': {
+ # "orderSessionId": order_session_id,
+ # "orderId": self.id,
+ # "checkoutToken": checkout_token,
+ # "browserIpAddress": browser_ip_address,
+ # "currency": self.partner_id.currency_id.name,
+ # "orderChannel": "WEB",
+ # "totalPrice": self.amount_total,
+ # 'products': [
+ # {
+ # "itemId": line.product_id.id,
+ # "itemName": line.product_id.name,
+ # "itemIsDigital": False,
+ # "itemCategory": line.product_id.categ_id.name,
+ # "itemUrl": line.product_id.website_url or '',
+ # "itemQuantity": line.product_uom_qty,
+ # "itemPrice": line.price_unit,
+ # "itemWeight": line.product_id.weight or 0.1,
+ # }
+ # for line in self.order_line if line.product_id
+ # ],
+ # 'shipments': [{
+ # "shipper": carrier.name,
+ # "shippingMethod": "ground",
+ # "shippingPrice": self.amount_delivery,
+ # }
+ # for carrier in self.carrier_id
+ # ],
+ # },
+ # 'recipients': [
+ # {
+ # "fullName": partner.name,
+ # "confirmationEmail": partner.email,
+ # "confirmationPhone": partner.phone,
+ # "organization": partner.company_id.name,
+ # "deliveryAddress": {
+ # "streetAddress": partner.street,
+ # "unit": partner.street2,
+ # "city": partner.city,
+ # "provinceCode": partner.state_id.code,
+ # "postalCode": partner.zip,
+ # "countryCode": partner.country_id.code,
+ # }
+ # }
+ # for partner in recipients
+ # ],
+ # 'transactions': [
+ # {
+ # "parentTransactionId": None,
+ # "transactionId": tx.id,
+ # "gateway": tx.acquirer_id.name,
+ # "paymentMethod": "CREDIT_CARD",
+ # "gatewayStatusCode": tx_status_type.get(tx.state, 'PENDING'),
+ # "type": "AUTHORIZATION",
+ # "currency": self.partner_id.currency_id.name,
+ # "amount": tx.amount,
+ # # "avsResponseCode": "Y",
+ # # "cvvResponseCode": "N",
+ # "checkoutPaymentDetails": {
+ # "holderName": tx.partner_id.name,
+ # "billingAddress": {
+ # "streetAddress": tx.partner_id.street,
+ # "unit": tx.partner_id.street2,
+ # "city": tx.partner_id.city,
+ # "provinceCode": tx.partner_id.state_id.code,
+ # "postalCode": tx.partner_id.zip,
+ # "countryCode": tx.partner_id.country_id.code,
+ # }
+ # }
+ # }
+ # for tx in self.transaction_ids
+ # ],
+ # }
return new_case_vals
diff --git a/website_sale_signifyd/models/signifyd_connector.py b/website_sale_signifyd/models/signifyd_connector.py
index c642fcc5..392a4e91 100644
--- a/website_sale_signifyd/models/signifyd_connector.py
+++ b/website_sale_signifyd/models/signifyd_connector.py
@@ -27,7 +27,12 @@ class SignifydConnector(models.Model):
('GUARANTEE', 'Guarantee'),
], string='Default Case Creation', help='Used for internal/admin orders, overridden by payment acquirer.',
required=True, default='')
- signifyd_coverage_ids = fields.Many2many('signifyd.coverage', string='Available Coverage Types')
+ signifyd_coverage_ids = fields.Many2many('signifyd.coverage', string='Available Coverage Types',
+ help='Note that exclusive coverage types will only allow one to be selected.')
+
+ @api.onchange('signifyd_coverage_ids')
+ def _onchange_signifyd_coverage_ids(self):
+ self.signifyd_coverage_ids = self.signifyd_coverage_ids._apply_exclusivity()
# TODO ideally this would be a regular constant
# however other entities currently use this by reference
diff --git a/website_sale_signifyd/models/signifyd_coverage.py b/website_sale_signifyd/models/signifyd_coverage.py
index 05c0e3c8..82a4739f 100644
--- a/website_sale_signifyd/models/signifyd_coverage.py
+++ b/website_sale_signifyd/models/signifyd_coverage.py
@@ -8,3 +8,7 @@ class SignifydCoverage(models.Model):
name = fields.Char(required=True)
description = fields.Char()
code = fields.Char(required=True)
+ exclusive = fields.Boolean())
+
+ def _apply_exclusivity(self):
+ return self.filtered('exclusive')[:1] or self
diff --git a/website_sale_signifyd/models/stock.py b/website_sale_signifyd/models/stock_picking.py
similarity index 100%
rename from website_sale_signifyd/models/stock.py
rename to website_sale_signifyd/models/stock_picking.py