Skip to content

Commit

Permalink
[Fix #29] Migration V13
Browse files Browse the repository at this point in the history
  • Loading branch information
Maxime Munier authored and Creamaster committed Jul 4, 2024
1 parent c00c4a1 commit f69ca67
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 179 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Payfip payment acquirer

## Install Odoo 11
## Install Odoo 13

- [Install Odoo 11.0](https://www.odoo.com/documentation/11.0/setup/install.html) on your favorite environment
- [Install Odoo 13.0](https://www.odoo.com/documentation/13.0/setup/install.html) on your favorite environment

## Install addon:

- Execute command :

```ruby
pip install "git+https://github.com/Horanet/payment_payfip.git@11.0#egg=odoo11-addon-payment-payfip&subdirectory=setup/payment_payfip"
pip install "git+https://github.com/Horanet/payment_payfip.git@13.0#egg=odoo13-addon-payment-payfip&subdirectory=setup/payment_payfip"
```
4 changes: 2 additions & 2 deletions payment_payfip/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
'name': "Intermédiaire de paiement PayFIP",
'version': '11.0.22.02.25',
'version': '13.0.1.0.0',
'summary': """Intermédiaire de paiement : Implémentation de PayFIP""",
'author': "Horanet",
'website': "http://www.horanet.com/",
'website': "https://www.horanet.com/",
'license': "AGPL-3",
'category': 'Accounting',
'external_dependencies': {
Expand Down
2 changes: 1 addition & 1 deletion payment_payfip/controllers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class PayFIPController(http.Controller):
def payfip_pay(self, **post):
reference = post.pop('reference', False)
amount = float(post.pop('amount', 0))
return_url = post.pop('return_url', '/shop/payment/validate') # /payment/process after v11
return_url = post.pop('return_url', '/payment/process')
tx = request.env['payment.transaction'].sudo().search([('reference', '=', reference), ('amount', '=', amount)])
if tx and tx.acquirer_id.provider == 'payfip':
# PayFIP doesn't accept two attempts with the same operation identifier, we check if transaction has
Expand Down
4 changes: 2 additions & 2 deletions payment_payfip/data/payment_acquirer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
<record id="payment_acquirer_payfip" model="payment.acquirer">
<field name="journal_id" eval="False"/>
<field name="name">PayFIP</field>
<field name="image" type="base64" file="payment_payfip/static/description/icon.png"/>
<field name="image_128" type="base64" file="payment_payfip/static/description/icon.png"/>
<field name="provider">payfip</field>
<field name="company_id" ref="base.main_company"/>
<field name="view_template_id" ref="payfip_form"/>
<field name="environment">test</field>
<field name="state">test</field>
<field name="pre_msg">
<![CDATA[<p>You will be redirected to the PayFIP website after clicking on the payment button.</p>]]>
</field>
Expand Down
193 changes: 130 additions & 63 deletions payment_payfip/i18n/fr.po

Large diffs are not rendered by default.

119 changes: 44 additions & 75 deletions payment_payfip/models/inherited_payment_acquirer.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,29 +49,25 @@ def _check_payfip_customer_number(self):
if not webservice_enabled:
raise ValidationError(message)

@api.constrains('environment') # environnment -> state in v13
def _check_environment(self):
@api.constrains('state') # website_published -> state in v13
def _check_state(self):
self.ensure_one()
if self.provider == 'payfip' and self.environment != 'test':
self.payfip_activation_mode = False

@api.constrains('website_published') # website_published -> state in v13
def _check_website_published(self):
self.ensure_one()
if self.provider == 'payfip' and self.website_published:
webservice_enabled, message = self._payfip_check_web_service()
if not webservice_enabled:
raise ValidationError(message)
self.payfip_activation_mode = False
if self.provider == 'payfip':
if self.state == 'enabled':
webservice_enabled, message = self._payfip_check_web_service()
if not webservice_enabled:
raise ValidationError(message)
self.payfip_activation_mode = False
elif self.state == 'test':
self.payfip_activation_mode = True
else:
self.payfip_activation_mode = False

@api.constrains('payfip_activation_mode')
def _check_payfip_activation_mode(self):
self.ensure_one()
if self.provider == 'payfip' and self.payfip_activation_mode and (
not self.website_published or self.environment not in ['test']):
raise ValidationError(_("PayFIP: activation mode can be activate in test environment only and if "
"the payment acquirer is published on the website."))

if self.provider == 'payfip' and self.payfip_activation_mode and self.state != 'test':
raise ValidationError(_("PayFIP: activation mode can be set in test environment only."))
# endregion

# region CRUD (overrides)
Expand Down Expand Up @@ -108,26 +104,26 @@ def _get_feature_support(self):
res['authorize'].append('payfip')
return res

@api.multi
def payfip_get_form_action_url(self):
self.ensure_one()
return '/payment/payfip/pay'

@api.multi
def payfip_get_id_op_from_web_service(self, email, price, object, acquirer_reference):
self.ensure_one()
id_op = ''
if self.state == 'disabled':
return id_op

mode = 'TEST'
if self.environment == 'prod':
if self.state == 'enabled':
mode = 'PRODUCTION'

base_url = self.env['ir.config_parameter'].get_param('web.base.url')
exer = fields.Datetime.now()[:4]
exer = fields.Date.today().year
numcli = self.payfip_customer_number
saisie = 'X' if self.payfip_activation_mode else ('T' if mode == 'TEST' else 'W')
urlnotif = '%s' % urllib.parse.urljoin(base_url, '/payment/payfip/ipn')
urlredirect = '%s' % urllib.parse.urljoin(base_url, '/payment/payfip/dpn')
urlnotif = f"{urllib.parse.urljoin(base_url, '/payment/payfip/ipn')}"
urlredirect = f"{urllib.parse.urljoin(base_url, '/payment/payfip/dpn')}"

soap_body = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" ' \
'xmlns:pai="http://securite.service.tpa.cp.finances.gouv.fr/services/mas_securite/' \
Expand Down Expand Up @@ -215,11 +211,11 @@ def payfip_get_result_from_web_service(self, idop):

response = root.find('.//return')
if response is None:
raise Exception("No result found for transaction with idOp: %s" % idop)
raise Exception(f"No result found for transaction with idOp: {idop}")

resultrans = response.find('resultrans')
if resultrans is None:
raise Exception("No result found for transaction with idOp: %s" % idop)
raise Exception(f"No result found for transaction with idOp: {idop}")

dattrans = response.find('dattrans')
heurtrans = response.find('heurtrans')
Expand Down Expand Up @@ -248,7 +244,6 @@ def payfip_get_result_from_web_service(self, idop):

return data

@api.multi
def _payfip_check_web_service(self):
self.ensure_one()

Expand Down Expand Up @@ -290,66 +285,40 @@ def _payfip_check_web_service(self):

return True, ''

@api.multi
def toggle_payfip_activation_mode_value(self):
in_activation = self.filtered(lambda acquirer: acquirer.payfip_activation_mode)
in_activation.write({'payfip_activation_mode': False})
(self - in_activation).write({'payfip_activation_mode': True})
in_activation = self.filtered('payfip_activation_mode')
in_activation.payfip_activation_mode = False
(self - in_activation).payfip_activation_mode = True

@api.model
def _get_errors_from_webservice(self, root):
errors = []

def get_error(error_value):
if error_value is not None:
code = error_value.find('code')
label = error_value.find('libelle')
description = error_value.find('descriptif')
severity = error_value.find('severite')
return [{
'code': code.text if code is not None else 'NC',
'label': label.text if label is not None else 'NC',
'description': description.text if description is not None else 'NC',
'severity': severity.text if severity is not None else 'NC',
}]
else:
return []

namespaces = self._get_soap_namespaces()
error_functionnal = root.find('.//ns1:FonctionnelleErreur', namespaces)
error_dysfonctionnal = root.find('.//ns1:TechDysfonctionnementErreur', namespaces)
error_unavailabilityl = root.find('.//ns1:TechIndisponibiliteErreur', namespaces)
error_protocol = root.find('.//ns1:TechProtocolaireErreur', namespaces)

if error_functionnal is not None:
code = error_functionnal.find('code')
label = error_functionnal.find('libelle')
description = error_functionnal.find('descriptif')
severity = error_functionnal.find('severite')
errors += [{
'code': code.text if code is not None else 'NC',
'label': label.text if label is not None else 'NC',
'description': description.text if description is not None else 'NC',
'severity': severity.text if severity is not None else 'NC',
}]
if error_dysfonctionnal is not None:
code = error_dysfonctionnal.find('code')
label = error_dysfonctionnal.find('libelle')
description = error_dysfonctionnal.find('descriptif')
severity = error_dysfonctionnal.find('severite')
errors += [{
'code': code.text if code is not None else 'NC',
'label': label.text if label is not None else 'NC',
'description': description.text if description is not None else 'NC',
'severity': severity.text if severity is not None else 'NC',
}]
if error_unavailabilityl is not None:
code = error_unavailabilityl.find('code')
label = error_unavailabilityl.find('libelle')
description = error_unavailabilityl.find('descriptif')
severity = error_unavailabilityl.find('severite')
errors += [{
'code': code.text if code is not None else 'NC',
'label': label.text if label is not None else 'NC',
'description': description.text if description is not None else 'NC',
'severity': severity.text if severity is not None else 'NC',
}]
if error_protocol is not None:
code = error_protocol.find('code')
label = error_protocol.find('libelle')
description = error_protocol.find('descriptif')
severity = error_protocol.find('severite')
errors += [{
'code': code.text if code is not None else 'NC',
'label': label.text if label is not None else 'NC',
'description': description.text if description is not None else 'NC',
'severity': severity.text if severity is not None else 'NC',
}]
errors += get_error(error_functionnal)
errors += get_error(error_dysfonctionnal)
errors += get_error(error_unavailabilityl)
errors += get_error(error_protocol)

return errors
# endregion
Expand Down
28 changes: 12 additions & 16 deletions payment_payfip/models/inherited_payment_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class PayFIPTransaction(models.Model):
('R', "Other cases (R)"),
('Z', "Other cases (Z)"),
('U', "Unknown"),
]
],
)

payfip_amount = fields.Float(
Expand All @@ -66,7 +66,8 @@ def create(self, vals):
prec = self.env['decimal.precision'].precision_get('Product Price')
email = res.partner_email
amount = int(float_round(res.amount * 100.0, prec))
reference = res.reference.replace('/', ' slash ')
# Les caractères spéciaux ne sont pas autorisés par Payfip dans l'objet
reference = res.reference.replace('/', ' slash ').replace('-', 'x')
acquirer_reference = '%.15d' % int(uuid.uuid4().int % 899999999999999)
res.acquirer_reference = acquirer_reference
idop = res.acquirer_id.payfip_get_id_op_from_web_service(email, amount, reference, acquirer_reference)
Expand Down Expand Up @@ -96,19 +97,18 @@ def _payfip_form_get_tx_from_data(self, idop):
raise ValidationError(error_msg)

# find tx -> @TDENOTE use txn_id ?
txs = self.env['payment.transaction'].sudo().search([('payfip_operation_identifier', '=', idop)])
if not txs or len(txs) > 1:
error_msg = 'PayFIP: received data for idop %s' % idop
error_msg += '; no order found' if not txs else '; multiple order found'
transaction_ids = self.env['payment.transaction'].sudo().search([('payfip_operation_identifier', '=', idop)])
if not transaction_ids or len(transaction_ids) > 1:
error_msg = f'PayFIP: received data for idop {idop}'
error_msg += '; multiple order found' if transaction_ids else '; no order found'
_logger.error(error_msg)
raise ValidationError(error_msg)
return txs[0]
return transaction_ids[0]

@api.multi
def _payfip_form_validate(self, idop):
self.ensure_one()

# If transaction is already done, we don't try to validate again.
# If the transaction is already done, we don't try to validate again.
if self.state in ['done']:
return True

Expand All @@ -121,7 +121,6 @@ def _payfip_form_validate(self, idop):

self._payfip_evaluate_data(data)

@api.multi
def _payfip_evaluate_data(self, data=False):
if not data:
return False
Expand Down Expand Up @@ -149,16 +148,13 @@ def _payfip_evaluate_data(self, data=False):
year = int(payfip_date[4:8])
hour = int(payfip_datetime[:2])
minute = int(payfip_datetime[2:4])
payfip_tz = pytz.timezone('Europe/Paris')
td_minute = timedelta(minutes=1)
date_validate = fields.Datetime.to_string(
datetime(year, month, day, hour=hour, minute=minute, tzinfo=payfip_tz) + td_minute
)
date_validate = datetime(year, month, day, hour=hour, minute=minute) + td_minute

self.write({
'state': 'done',
'payfip_state': result,
'date_validate': date_validate,
'date': date_validate,
'payfip_amount': payfip_amount,
})
return True
Expand Down Expand Up @@ -223,7 +219,7 @@ def payfip_cron_check_draft_payment_transactions(self, options=None):
('acquirer_id', 'in', payfip_acquirers.ids),
('state', 'in', ['draft', 'pending']),
('payfip_operation_identifier', 'not in', [False, '']),
('create_date', '>=', fields.Datetime.to_string(date_from)), # No longer cast to string in v13
('create_date', '>=', date_from),
])

for tx in transactions:
Expand Down
35 changes: 18 additions & 17 deletions payment_payfip/views/payment_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,24 @@
<field name="payfip_activation_mode" invisible="1" />
</group>
</xpath>
<xpath expr='//button[@name="toggle_website_published"]'
position='before'>
<button attrs="{'invisible': ['|', ('provider', '!=', 'payfip'), ('payfip_activation_mode', '=', True)]}"
class="oe_stat_button"
name="toggle_payfip_activation_mode_value" type="object" icon="fa-check-square-o">
<div class="o_stat_info o_form_field">
<span class="text-success">Not in activation</span>
</div>
</button>
<button attrs="{'invisible': ['|', ('provider', '!=', 'payfip'), ('payfip_activation_mode', '=', False)]}"
class="oe_stat_button"
name="toggle_payfip_activation_mode_value" type="object" icon="fa-square-o">
<div class="o_stat_info o_form_field">
<span class="o_warning_text">In activation</span>
</div>
</button>
</xpath>
<field name="image_128" position='before'>
<div class="oe_button_box" name="button_box">
<button attrs="{'invisible': ['|', ('provider', '!=', 'payfip'), ('payfip_activation_mode', '=', True)]}"
class="oe_stat_button"
name="toggle_payfip_activation_mode_value" type="object" icon="fa-check-square-o">
<div class="o_stat_info o_form_field">
<span class="text-success">Not in activation</span>
</div>
</button>
<button attrs="{'invisible': ['|', ('provider', '!=', 'payfip'), ('payfip_activation_mode', '=', False)]}"
class="oe_stat_button"
name="toggle_payfip_activation_mode_value" type="object" icon="fa-square-o">
<div class="o_stat_info o_form_field">
<span class="o_warning_text">In activation</span>
</div>
</button>
</div>
</field>
</field>
</record>

Expand Down

0 comments on commit f69ca67

Please sign in to comment.