149 lines
7.1 KiB
Python
149 lines
7.1 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
import datetime
|
|
import requests
|
|
import pytz
|
|
from urllib.parse import urljoin
|
|
|
|
from odoo import _, api, fields, models
|
|
from odoo.exceptions import ValidationError
|
|
|
|
QRIS_TIMEOUT = 35 # They say that the time to get a response vary between 6 to 30s
|
|
|
|
|
|
def _l10n_id_make_qris_request(endpoint, params):
|
|
""" Make an API request to QRIS, using the given path and params. """
|
|
url = urljoin('https://qris.online/restapi/qris/', endpoint)
|
|
try:
|
|
response = requests.get(url, params=params, timeout=QRIS_TIMEOUT)
|
|
response.raise_for_status()
|
|
response = response.json()
|
|
except requests.exceptions.HTTPError as err:
|
|
raise ValidationError(_("Communication with QRIS failed. QRIS returned with the following error: %s", err))
|
|
except (requests.RequestException, ValueError):
|
|
raise ValidationError(_("Could not establish a connection to the QRIS API."))
|
|
|
|
return response
|
|
|
|
|
|
class ResBank(models.Model):
|
|
_inherit = "res.partner.bank"
|
|
|
|
l10n_id_qris_api_key = fields.Char("QRIS API Key", groups="base.group_system")
|
|
l10n_id_qris_mid = fields.Char("QRIS Merchant ID", groups="base.group_system")
|
|
|
|
@api.model
|
|
def _get_available_qr_methods(self):
|
|
# EXTENDS account
|
|
rslt = super()._get_available_qr_methods()
|
|
rslt.append(('id_qr', _("QRIS"), 40))
|
|
return rslt
|
|
|
|
def _get_error_messages_for_qr(self, qr_method, debtor_partner, currency):
|
|
# EXTENDS account
|
|
if qr_method == 'id_qr':
|
|
if self.country_code != 'ID':
|
|
return _("You cannot generate a QRIS QR code with a bank account that is not in Indonesia.")
|
|
if currency.name not in ['IDR']:
|
|
return _("You cannot generate a QRIS QR code with a currency other than IDR")
|
|
if not (self.l10n_id_qris_api_key and self.l10n_id_qris_mid):
|
|
return _("To use QRIS QR code, Please setup the QRIS API Key and Merchant ID on the bank's configuration")
|
|
return None
|
|
|
|
return super()._get_error_messages_for_qr(qr_method, debtor_partner, currency)
|
|
|
|
def _check_for_qr_code_errors(self, qr_method, amount, currency, debtor_partner, free_communication, structured_communication):
|
|
# EXTENDS account
|
|
if qr_method == 'id_qr':
|
|
if not amount:
|
|
return _("The amount must be set to generate a QR code.")
|
|
|
|
return super()._check_for_qr_code_errors(qr_method, amount, currency, debtor_partner, free_communication, structured_communication)
|
|
|
|
def _get_qr_vals(self, qr_method, amount, currency, debtor_partner, free_communication, structured_communication):
|
|
""" Getting content for the QR through calling QRIS API and storing the QRIS transaction as a record"""
|
|
# EXTENDS account
|
|
if qr_method == "id_qr":
|
|
model = self._context.get('qris_model')
|
|
model_id = self._context.get('qris_model_id')
|
|
|
|
# qris_trx is to help us fetch the backend record associated to the model and model_id.
|
|
# we are using model and model_id instead of model.browse(id) because while executing this method
|
|
# not all backend records are created already. For example, pos.order record isn't created until
|
|
# payment is completed on the PoS interace.
|
|
qris_trx = self.env['l10n_id.qris.transaction']._get_latest_transaction(model, model_id)
|
|
|
|
# QRIS codes are valid for 30 minutes. To leave some margin, we will return the same QR code we already
|
|
# generated if the invoice is re-accessed before 25m. Otherwise, a new QR code is generated
|
|
# Additionally, we want to check that it's requesting for the same amount as it's possible to change
|
|
# amount in apps like PoS.
|
|
if qris_trx and qris_trx.qris_amount == int(amount):
|
|
now = fields.Datetime.now()
|
|
latest_qr_date = qris_trx.qris_creation_datetime
|
|
|
|
if (now - latest_qr_date).total_seconds() < 1500:
|
|
return qris_trx['qris_content']
|
|
|
|
params = {
|
|
"do": "create-invoice",
|
|
"apikey": self.l10n_id_qris_api_key,
|
|
"mID": self.l10n_id_qris_mid,
|
|
"cliTrxNumber": structured_communication,
|
|
"cliTrxAmount": int(amount)
|
|
}
|
|
response = _l10n_id_make_qris_request('show_qris.php', params)
|
|
if response.get("status") == "failed":
|
|
raise ValidationError(response.get("data"))
|
|
data = response.get('data')
|
|
|
|
# create a new transaction line while also converting the qris_request_date to UTC time
|
|
if model and model_id:
|
|
new_trx = self.env['l10n_id.qris.transaction'].create({
|
|
'model': model,
|
|
'model_id': model_id,
|
|
'qris_invoice_id': data.get('qris_invoiceid'),
|
|
'qris_amount': int(amount),
|
|
# Since the QRIS response is always returned with "Asia/Jakarta" timezone which is UTC+07:00
|
|
'qris_creation_datetime': fields.Datetime.to_datetime(data.get('qris_request_date')) - datetime.timedelta(hours=7),
|
|
'qris_content': data.get('qris_content'),
|
|
'bank_id': self.id
|
|
})
|
|
|
|
# Search the backend record and attach the qris transaction to the record if it exists.
|
|
trx_record = new_trx._get_record()
|
|
if trx_record:
|
|
trx_record.l10n_id_qris_transaction_ids |= new_trx
|
|
|
|
return data.get('qris_content')
|
|
|
|
return super()._get_qr_vals(qr_method, amount, currency, debtor_partner, free_communication, structured_communication)
|
|
|
|
def _get_qr_code_generation_params(self, qr_method, amount, currency, debtor_partner, free_communication, structured_communication):
|
|
# EXTENDS account
|
|
if qr_method == 'id_qr':
|
|
if not self._context.get('is_online_qr'):
|
|
return {}
|
|
return {
|
|
'barcode_type': 'QR',
|
|
'width': 120,
|
|
'height': 120,
|
|
'value': self._get_qr_vals(qr_method, amount, currency, debtor_partner, free_communication, structured_communication),
|
|
}
|
|
return super()._get_qr_code_generation_params(qr_method, amount, currency, debtor_partner, free_communication, structured_communication)
|
|
|
|
def _l10n_id_qris_fetch_status(self, qr_data):
|
|
"""
|
|
using self and the given data, fetches the status of a specific QR code generated by QRIS
|
|
Expected values in the qr_data dict are:
|
|
- invoice_id returned when generating a QR code
|
|
- the amount present in the qr code
|
|
- the datetime at which the QR code was generated
|
|
"""
|
|
return _l10n_id_make_qris_request('checkpaid_qris.php', {
|
|
'do': 'checkStatus',
|
|
'apikey': self.l10n_id_qris_api_key,
|
|
'mID': self.l10n_id_qris_mid,
|
|
'invid': qr_data['qris_invoice_id'],
|
|
'trxvalue': qr_data['qris_amount'],
|
|
'trxdate': qr_data['qris_creation_datetime'],
|
|
})
|