[MIG] website_sale_signifyd: 1.1.0 (from 14)

This commit is contained in:
Jared Kipe
2021-12-02 12:19:08 -08:00
parent f7a513d5ee
commit 3bf68fcca8
12 changed files with 108 additions and 12 deletions

View File

@@ -1,7 +1,7 @@
{
'name': 'Signifyd Connector',
'author': 'Hibou Corp. <hello@hibou.io>',
'version': '15.0.1.0.0',
'version': '15.0.1.1.0',
'category': 'Sale',
'description': """
Automate Order Fraud Detection with the Signifyd API.
@@ -12,10 +12,12 @@ Automate Order Fraud Detection with the Signifyd API.
'hibou_professional',
'stock',
'website_sale',
'website_payment',
],
'data': [
'security/ir.model.access.csv',
'views/partner_views.xml',
'views/payment_views.xml',
'views/sale_views.xml',
'views/signifyd_views.xml',
'views/stock_views.xml',

View File

@@ -0,0 +1,9 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
def migrate(cr, version):
cr.execute('''
UPDATE signifyd_case
SET checkpoint_action = 'ACCEPT'
WHERE guarantee_disposition = 'APPROVED'
''')

View File

@@ -1,6 +1,7 @@
# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
from . import partner
from . import payment
from . import sale_order
from . import signifyd
from . import signifyd_connector

View File

@@ -0,0 +1,14 @@
# 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'
signifyd_case_type = fields.Selection([
('', 'No Case'),
('SCORE', 'Score'),
('DECISION', 'Decision'),
('GUARANTEE', 'Guarantee'),
], string='Signifyd Case Creation', default='')

View File

@@ -10,7 +10,7 @@ class SaleOrder(models.Model):
signifyd_case_id = fields.Many2one('signifyd.case', readonly=1, copy=False)
singifyd_score = fields.Float(related='signifyd_case_id.score')
signifyd_disposition_status = fields.Selection(related='signifyd_case_id.guarantee_disposition')
signifyd_checkpoint_action = fields.Selection(string='Signifyd Action', related='signifyd_case_id.checkpoint_action')
def action_view_signifyd_case(self):
self.ensure_one()
@@ -35,7 +35,13 @@ class SaleOrder(models.Model):
return res
def _should_post_signifyd(self):
return self.state in ('sale', 'done') and not self.signifyd_case_id
# 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
def post_signifyd_case(self):
if not self.website_id.signifyd_connector_id:
@@ -66,6 +72,18 @@ class SaleOrder(models.Model):
@api.model
def _prepare_signifyd_case_values(self, order_session_id, checkout_token, browser_ip_address):
decision_request = self.website_id.signifyd_connector_id.signifyd_case_type or 'DECISION'
# find the highest 'acquirer override'
# note that we shouldn't be here if the override would prevent sending
a_case_types = self.transaction_ids.mapped('acquirer_id.signifyd_case_type')
if a_case_types and 'GUARANTEE' in a_case_types:
decision_request = 'GUARANTEE'
elif a_case_types and 'SCORE' in a_case_types:
decision_request = 'SCORE'
elif a_case_types and 'DECISION' in a_case_types:
decision_request = 'DECISION'
tx_status_type = {
'draft': 'FAILURE',
'pending': 'PENDING',
@@ -77,7 +95,7 @@ class SaleOrder(models.Model):
recipients = self.partner_invoice_id + self.partner_shipping_id
new_case_vals = {
'decisionRequest': {
'paymentFraud': 'GUARANTEE',
'paymentFraud': decision_request,
},
'purchase': {
"orderSessionId": order_session_id,

View File

@@ -47,6 +47,11 @@ class SignifydCase(models.Model):
score = fields.Float(string='Transaction Score')
adjusted_score = fields.Float(string='Adjusted Score')
signifyd_url = fields.Char('Signifyd.com', compute='_compute_signifyd_url')
checkpoint_action = fields.Selection([
('ACCEPT', 'Accept'),
('HOLD', 'Hold'),
('REJECT', 'Reject'),
], string='Checkpoint Action')
def _get_connector(self):
return self.order_id.website_id.signifyd_connector_id
@@ -115,6 +120,14 @@ class SignifydCase(models.Model):
guarantee_disposition = case.get('guaranteeDisposition', self.guarantee_disposition)
adjusted_score = case.get('adjustedScore', self.adjusted_score)
score = case.get('score', self.score)
checkpoint_action = case.get('checkpointAction', self.checkpoint_action)
if not checkpoint_action and guarantee_disposition:
if guarantee_disposition == 'APPROVED':
checkpoint_action = 'ACCEPT'
elif guarantee_disposition == 'DECLINED':
checkpoint_action = 'REJECT'
else:
checkpoint_action = 'HOLD'
vals = {
'case_id': case_id,
@@ -128,13 +141,15 @@ class SignifydCase(models.Model):
'guarantee_disposition': guarantee_disposition,
'score': score,
'last_update': dt.now(), # why not just use
'checkpoint_action': checkpoint_action,
}
outcome = vals.get('guarantee_disposition')
if outcome == 'DECLINED':
checkpoint_action = vals.get('checkpoint_action')
if outcome == 'DECLINED' or checkpoint_action == 'REJECT':
connector = self._get_connector()
for user in connector.notify_user_ids:
self.create_notification(user, outcome)
self.create_notification(user, outcome or checkpoint_action)
self.write(vals)

View File

@@ -19,6 +19,13 @@ class SignifydConnector(models.Model):
webhooks_registered = fields.Boolean(string='Successfully Registered Webhooks')
notify_user_ids = fields.Many2many('res.users', string='Receive decline notifications')
website_ids = fields.One2many('website', 'signifyd_connector_id', string='Used on Websites')
signifyd_case_type = fields.Selection([
('', 'No Case'),
('SCORE', 'Score'),
('DECISION', 'Decision'),
('GUARANTEE', 'Guarantee'),
], string='Default Case Creation', help='Used for internal/admin orders, overridden by payment acquirer.',
required=True, default='')
# TODO ideally this would be a regular constant
# however other entities currently use this by reference
@@ -73,6 +80,10 @@ class SignifydConnector(models.Model):
"event": "GUARANTEE_COMPLETION",
"url": base_url + "/signifyd/cases/update"
},
{
"event": "DECISION_MADE",
"url": base_url + "/signifyd/cases/update"
},
]
}
data = json.dumps(values, indent=4)
@@ -127,6 +138,14 @@ class SignifydConnector(models.Model):
score = post.get('score')
disposition_reason = post.get('dispositionReason')
disposition = post.get('disposition')
checkpoint_action = post.get('checkpointAction', '')
if not checkpoint_action and guarantee_disposition:
if guarantee_disposition == 'APPROVED':
checkpoint_action = 'ACCEPT'
elif guarantee_disposition == 'DECLINED':
checkpoint_action = 'REJECT'
else:
checkpoint_action = 'HOLD'
last_update = str(dt.now())
values = {}
@@ -144,5 +163,6 @@ class SignifydConnector(models.Model):
values.update({'disposition_reason': disposition_reason}) if disposition_reason else ''
values.update({'disposition': disposition}) if disposition else ''
values.update({'last_update': last_update})
values.update({'checkpoint_action': checkpoint_action})
return values

View File

@@ -7,7 +7,7 @@ class StockPicking(models.Model):
_inherit = 'stock.picking'
singifyd_case_id = fields.Many2one(related='sale_id.signifyd_case_id')
signifyd_hold = fields.Selection(related='sale_id.signifyd_disposition_status')
signifyd_hold = fields.Selection(related='sale_id.signifyd_checkpoint_action')
def action_view_signifyd_case(self):
self.ensure_one()

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="acquirer_form_website_inherit" model="ir.ui.view">
<field name="name">acquirer.form.inherit.website.inherit</field>
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="website_payment.acquirer_form_website"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='website_id']" position="after">
<field name="signifyd_case_type"/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -28,7 +28,7 @@
</xpath>
<xpath expr="//field[@name='payment_term_id']" position="after">
<field name="signifyd_case_id" attrs="{'invisible': [('signifyd_case_id', '=', False)]}"/>
<field name="signifyd_disposition_status" string="Signifyd Status"
<field name="signifyd_checkpoint_action" string="Signifyd Status"
attrs="{'invisible': [('signifyd_case_id', '=', False)]}"/>
</xpath>
</field>
@@ -40,7 +40,7 @@
<field name="inherit_id" ref="sale.view_order_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='invoice_status']" position="after">
<field name="signifyd_disposition_status"/>
<field name="signifyd_checkpoint_action"/>
</xpath>
</field>
</record>
@@ -50,7 +50,7 @@
<field name="res_model">sale.order</field>
<field name="default" eval="True"/>
<field name="description">Signifyd Status Updated</field>
<field name="relation_field">signifyd_case_id.guarantee_disposition</field>
<field name="relation_field">signifyd_case_id.checkpoint_action</field>
</record>
</odoo>

View File

@@ -37,6 +37,7 @@
<group>
<group>
<field name="checkpoint_action"/>
<field name="last_update"/>
<field name="uuid"/>
<field name="case_id"/>
@@ -105,6 +106,7 @@
<group>
<field name="secret_key" attrs="{'invisible': [('test_mode', '=', True)]}"/>
<field name="secret_key_test" attrs="{'invisible': [('test_mode', '!=', True)]}"/>
<field name="signifyd_case_type" />
<p class="text-muted">
Optional: Add users to be notified if a sale order is declined by Signifyd.
</p>

View File

@@ -10,10 +10,10 @@
<field name="signifyd_hold" invisible="1"/>
<field name="singifyd_case_id" invisible="1"/>
<button name="action_view_signifyd_case" string="On Hold" type="object" class="oe_stat_button text-danger"
icon="fa-hand-stop-o" attrs="{'invisible': [ '|', ('signifyd_hold', '=', 'APPROVED'), ('singifyd_case_id', '=', False)]}"/>
icon="fa-hand-stop-o" attrs="{'invisible': [ '|', ('signifyd_hold', '=', 'ACCEPT'), ('singifyd_case_id', '=', False)]}"/>
<button name="action_view_signifyd_case" string="Approved" type="object" class="oe_stat_button text-success"
icon="fa-thumbs-o-up" attrs="{'invisible': [ '|', ('signifyd_hold', '!=', 'APPROVED'), ('singifyd_case_id', '=', False)]}"/>
icon="fa-thumbs-o-up" attrs="{'invisible': [ '|', ('signifyd_hold', '!=', 'ACCEPT'), ('singifyd_case_id', '=', False)]}"/>
</xpath>
</field>