Files
suite/website_sale_signifyd/models/signifyd_case.py
2024-12-09 23:59:04 +00:00

207 lines
8.8 KiB
Python

# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details.
import requests
import json
from datetime import datetime as dt
from odoo import api, fields, models, _
from odoo.exceptions import UserError
class SignifydCase(models.Model):
_name = 'signifyd.case'
_description = 'Stores Signifyd case information on orders.'
connector_id = fields.Many2one('signifyd.connector', string='Signifyd Connector')
order_id = fields.Many2one('sale.order', required=True)
partner_id = fields.Many2one('res.partner')
case_id = fields.Char(string='Case ID')
ref = fields.Char(string='Reference', help="Signifyd Order ID, set to the sale order's name for new cases. Previously Unique ID.")
status = fields.Selection([
('OPEN', 'Open'),
('DISMISSED', 'Dismissed'),
], string='Case Status')
name = fields.Char(string='Headline')
team_name = fields.Char(string='Team Name')
team_id = fields.Char(string='Team ID')
last_update = fields.Date('Last Update')
review_disposition = fields.Selection([
('UNSET', 'Pending'),
('FRAUD', 'Fraudulent'),
('GOOD', 'Good'),
], string='Review Status')
order_outcome = fields.Selection([
('PENDING', 'pending'),
('SUCCESSFUL', 'Successful'),
])
guarantee_disposition = fields.Selection([
('IN_REVIEW', 'Reviewing'),
('PENDING', 'Pending'),
('APPROVED', 'Approved'),
('DECLINED', 'Declined'),
('CANCELED', 'Canceled'),
], string='Guarantee Status')
disposition_reason = fields.Char('Disposition Reason')
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')
coverage_ids = fields.Many2many('signifyd.coverage', string='Coverage', help='Accepted Coverage Types')
coverage_line_ids = fields.One2many('signifyd.case.coverage', 'case_id')
@api.model
def _compute_signifyd_url(self):
for record in self:
if record.case_id:
self.signifyd_url = 'https://app.signifyd.com/cases/%s' % (record.case_id, )
else:
self.signifyd_url = ''
def write(self, vals):
original_disposition = {c: c.guarantee_disposition for c in self}
res = super(SignifydCase, self).write(vals)
disposition = vals.get('guarantee_disposition', False)
for case in self.filtered(lambda c: c.order_id and original_disposition[c] != disposition):
case.order_id.message_post(body=_('Signifyd Updated Record to %s' % disposition),
subtype_xmlid='website_sale_signifyd.disposition_change')
return res
# @api.model
# def post_case(self, connector, values):
# headers = connector.get_headers()
# # data = json.dumps(values, indent=4, sort_keys=True, default=str)
# url = connector.API_URL + '/orders/events/sales'
# # TODO this should be in `signifyd.connector`
# r = requests.post(url=url, headers=headers, json=values)
# return r.json()
# def get_case(self):
# self.ensure_one()
# if not self.case_id:
# raise UserError(_('Case not represented in Signifyd.'))
# connector = self._get_connector()
# headers = connector.get_headers()
# r = requests.get(
# connector.API_URL + '/cases/' + str(self.case_id),
# headers=headers
# )
# return r.json()
def get_decision_vals(self):
self.ensure_one()
api = self.connector_id.get_connection()
response = api.get_decision(self.ref)
response.raise_for_status()
data = response.json()
decision = data['decision']
case_vals = {
'last_update': decision.get('createdAt'),
'checkpoint_action': decision.get('checkpointAction'),
'score': decision.get('score'),
'coverage_line_ids': []
}
coverage = data.get('coverage')
if coverage:
coverage_map = {
'inrChargebacks': self.env.ref('website_sale_signifyd.signifyd_coverage_inr').id,
'fraudChargebacks': self.env.ref('website_sale_signifyd.signifyd_coverage_fraud').id,
'snadChargebacks': self.env.ref('website_sale_signifyd.signifyd_coverage_snad').id,
'allChargebacks': self.env.ref('website_sale_signifyd.signifyd_coverage_all').id
}
# coverage_ids = []
# if coverage.get('inrChargebacks'):
# coverage_ids += self.env.ref('website_sale_signifyd.signifyd_coverage_inr').ids
# if coverage.get('fraudChargebacks'):
# coverage_ids += self.env.ref('website_sale_signifyd.signifyd_coverage_fraud').ids
# if coverage.get('snadChargebacks'):
# coverage_ids += self.env.ref('website_sale_signifyd.signifyd_coverage_snad').ids
# if coverage.get('allChargebacks'):
# coverage_ids = self.env.ref('website_sale_signifyd.signifyd_coverage_all').ids
# vals['coverage_ids'] = coverage_ids
for name, vals in coverage.items():
if not vals:
continue
case_vals['coverage_line_ids'] += [(0, 0, {
# 'case_id': self.id,
'coverage_type_id': coverage_map[name],
'amount': vals.get('amount'),
'currency_id': self.env['res.currency'].search(
[('name', '=ilike', vals.get('currency'))], limit=1).id,
})]
return {k: v for k, v in case_vals.items() if v}
def action_force_update_case(self):
for record in self:
record.update_case_info()
def update_case_info(self, vals=None):
self.ensure_one()
if not self.case_id:
raise UserError(_('Case not represented in Signifyd.'))
if not vals:
vals = self.get_decision_vals()
# case_id = case.get('caseId')
# if not case_id:
# raise ValueError(_('Signifyd Case has no ID?'))
# team_id = case.get('teamId', self.team_id)
# team_name = case.get('teamName', self.team_name)
# ref = case.get('ref', self.ref)
# status = case.get('status', self.status)
# review_disposition = case.get('reviewDisposition', self.review_disposition)
# order_outcome = case.get('orderOutcome', self.order_outcome)
# 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,
# 'team_id': team_id,
# 'team_name': team_name,
# 'ref': ref,
# 'status': status,
# 'review_disposition': review_disposition,
# 'order_outcome': order_outcome,
# 'adjusted_score': adjusted_score,
# 'guarantee_disposition': guarantee_disposition,
# 'score': score,
# 'last_update': dt.now(), # why not just use
# 'checkpoint_action': checkpoint_action,
# }
outcome = vals.get('guarantee_disposition')
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 or checkpoint_action)
self.sudo().write(vals)
def create_notification(self, user, outcome):
self.ensure_one()
vals = {
'summary': 'Signifyd Case %s %s' % (self.case_id, outcome),
'activity_type_id': self.env.ref('mail.mail_activity_data_todo').id,
'user_id': user.id,
'res_id': self.order_id.id,
'res_model_id': self.env['ir.model']._get(self.order_id._name).id,
}
self.env['mail.activity'].create(vals)